U
U
Urvin2019-01-23 13:16:49
PostgreSQL
Urvin, 2019-01-23 13:16:49

How to cleverly paginate data in PostgreSql?

Let's say we have a table

create table car
(
  id serial constraint car_pk primary key,
  vendor_name varchar not null,
  model_name varchar not null,
  body_type varchar not null,
  specifications_name varchar not null,
  price int4 not null
);

And let it fill like this:
SQL
INSERT INTO car(vendor_name, model_name, body_type, specifications_name, price) VALUES
('Peugeot', '408', 'Седан', 'Allure 115лс бежевый', 1144000),
('LADA', 'Vesta', 'Седан', 'Luxe морская волна', 635000),
('Ford', 'Focus', 'Хэтчбэк', 'Sync сепый', 1109000),
('Ford', 'Focus', 'Седан', 'Sync белый', 1250800),
('LADA', 'Vesta', 'Седан', 'Сlassic зеленый', 631800),
('Audi', 'A4', 'Универсал', 'Желтый', 2900000),
('Ford', 'Focus', 'Хэтчбэк', 'Special мандариноый', 1126000),
('LADA', 'Granta', 'Седан', 'Comfort серый', 520000),
('LADA', 'Vesta', 'Седан', 'Сomfort синий', 631100),
('Ford', 'Focus', 'Седан', 'Trend синий', 1235000),
('LADA', 'Vesta', 'Универсал', 'Comfort оранжевый', 679000),
('Audi', 'A4', 'Седан', 'Желтый', 2000000),
('LADA', 'Granta', 'Седан', 'Luxe Prestige зеленый', 576000),
('Peugeot', '408', 'Седан', 'Active красный', 1177000),
('Audi', 'A4', 'Седан', 'Желтый', 2000000),
('Ford', 'Focus', 'Седан', 'Special мандариноый', 1203000),
('LADA', 'Granta', 'Седан', 'Luxe серый', 531000),
('Peugeot', '408', 'Седан', 'Allure 150лс белый', 1122000),
('Audi', 'A4', 'Универсал', 'Серый', 2900000),
('LADA', 'Vesta', 'Универсал', 'Luxe белый', 680000),
('Ford', 'Focus', 'Седан', 'Special апельсиновый', 1211000),
('Ford', 'Focus', 'Хэтчбэк', 'Special апельсиновый', 1125000),
('LADA', 'Vesta', 'Универсал', 'Comfort виноградный', 630000),
('Peugeot', '408', 'Седан', 'Allure 150лс пурпурный', 1125000),
('Audi', 'A3', 'Хетчбек', 'Белый', 2000000),
('Ford', 'Focus', 'Хэтчбэк', 'Special лимонный', 1088000),
('LADA', 'Vesta', 'Универсал', 'Luxe синиый', 699000),
('Ford', 'Focus', 'Седан', 'Trend зеленый', 1230000),
('LADA', 'Vesta', 'Седан', 'Luxe вердепомовый', 634000),
('Ford', 'Focus', 'Седан', 'Sync сепый', 1260000),
('LADA', 'Granta', 'Универсал', 'Comfort малиновый', 566000),
('LADA', 'Granta', 'Седан', 'Comfort красный', 520000),
('LADA', 'Vesta', 'Седан', 'Сlassic бежевый', 631000),
('Ford', 'Focus', 'Седан', 'Special лимонный', 1201000),
('Ford', 'Focus', 'Хэтчбэк', 'Trend синий', 1065000),
('LADA', 'Vesta', 'Универсал', 'Luxe красный', 679000),
('LADA', 'Granta', 'Универсал', 'Standart белый', 520000),
('Audi', 'A4', 'Универсал', 'Черный', 3000000),
('LADA', 'Vesta', 'Седан', 'Сomfort восхитительный', 641000),
('Ford', 'Focus', 'Седан', 'Sync черный', 1250000),
('LADA', 'Granta', 'Седан', 'Standart черный', 438000),
('Audi', 'A3', 'Хетчбек', 'Желтый', 2000000),
('LADA', 'Granta', 'Универсал', 'Standart черный', 465030),
('LADA', 'Vesta', 'Седан', 'Сlassic белый', 638005),
('LADA', 'Granta', 'Универсал', 'Standart синий', 485000),
('LADA', 'Granta', 'Универсал', 'Comfort мокрый асфальт', 566000),
('Audi', 'A4', 'Универсал', 'Белый', 2900000),
('Ford', 'Focus', 'Хэтчбэк', 'Trend белый', 1027000),
('LADA', 'Granta', 'Седан', 'Standart синий', 438000),
('LADA', 'Granta', 'Универсал', 'Luxe пурпурный', 662000),
('LADA', 'Vesta', 'Универсал', 'Comfort желтый', 679010),
('Ford', 'Focus', 'Седан', 'Trend белый', 1230000),
('Audi', 'A3', 'Хетчбек', 'Черный', 2000000),
('LADA', 'Granta', 'Универсал', 'Comfort голубой', 566000),
('LADA', 'Granta', 'Универсал', 'Luxe коралловый', 662080),
('LADA', 'Granta', 'Универсал', 'Luxe аквамарин', 662100),
('LADA', 'Vesta', 'Седан', 'Сomfort абрикосовый', 631000),
('LADA', 'Vesta', 'Седан', 'Luxe синий', 636000),
('Ford', 'Focus', 'Хэтчбэк', 'Sync черный', 1082000),
('Ford', 'Focus', 'Хэтчбэк', 'Sync белый', 1092000)
;

I have to sort the table so that:
- Cars of the vendor with the lowest price go first
- Inside vendor - sort by lowest model price
- Inside model - by lowest price of body type
- next by price and specification name:
SELECT
  *,
  MIN(price) OVER win_vendor min_price_vendor,
  MIN(price) OVER win_model min_price_model,
  MIN(price) OVER win_body min_price_body
FROM
  car
WINDOW
  win_vendor AS (PARTITION BY vendor_name),
  win_model AS (PARTITION BY vendor_name, model_name),
  win_body AS (PARTITION BY vendor_name, model_name, body_type)
ORDER BY
  min_price_vendor,
  min_price_model,
  min_price_body,
  price,
  specifications_name

And now pagination.
I need to cut pages not with a simple Limit / Offset, but in such a way that the beginning or end of the page will certainly be at the beginning or end of the list of vehicles of the vendor-model-body_type. At the same time, the page should contain, if possible, at least N records (or no more, if it is simpler).
Let, N=10
5c483c9cf2de4129268059.png
Pages on 15, 15, 17, 13 records turn out. I expect the formation of a new page_number column, so that I write WHERE page_number = K in queries to pull out a portion of data from the K-th page.
Please help me to calculate the page number in SQL

Answer the question

In order to leave comments, you need to log in

2 answer(s)
D
d-stream, 2019-01-23
@d-stream

ROWS | RANGE pg can?
Well, or at least row_number in the select, which can be turned into sub_page_number within the window (for the situation when there are 20 pages) according to your criterion nm rows per page

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question