2016-05-02 2 views
2

Я хотел бы включить столбец row_number в свой результирующий набор с последовательностью номеров строк, где 1 - это новейший элемент без пробелов. Это работает:Глобальные номера строк в chunked запросе

SELECT id, row_number() over (ORDER BY id desc) AS row_number, title 
FROM mytable 
WHERE group_id = 10; 

Теперь я хотел бы запросить одни и те же данные в кусках 1000 каждого, чтобы быть легче на памяти:

SELECT id, row_number() over (ORDER BY id desc) AS row_number, title 
FROM mytable 
WHERE group_id = 10 AND id >= 0 AND id < 1000 
ORDER BY id ASC; 

Здесь row_number перезапусков от 1 для каждого фрагмента, но я хотел бы, чтобы это было так, как если бы оно было частью глобального запроса, как в первом случае. Есть ли простой способ сделать это?

ответ

1

Предполагая:

  • id определяется как PRIMARY KEY - что означает UNIQUE и NOT NULL. Возможно, вам придется иметь дело со значениями NULL и/или дубликатами (связями).

  • У вас нет одновременного доступа на запись на столе - или вам все равно, что произойдет после того, как вы сделали снимок.

MATERIALIZED VIEW, как вы демонстрируете in your answer, является хорошим выбором.

CREATE MATERIALIZED VIEW mv_temp AS 
SELECT row_number() OVER (ORDER BY id DESC) AS rn, id, title 
FROM mytable 
WHERE group_id = 10; 

Но индекс и последующие запросы должны быть на номер строки rn получить

данные куски из 1000

CREATE INDEX ON mv_temp (rn); 

SELECT * FROM mv_temp WHERE rn BETWEEN 1000 AND 2000;

Ваша реализация потребует гарантированный зазор меньше id - что лишит необходимости добавления номера строки для начала ...

Когда сделано:

DROP MATERIALIZED VIEW mv_temp; 

Индекс умирает с таблицей (материализованное представление в данном случае) автоматически.

Связанных с более подробной информацией:

+0

Ха-ха, я собирался принять свой собственный ответ, но вы переписали его с большей осторожностью и детализацией, интересной ситуацией. Для моего варианта использования не имеет значения, выполняется ли index/query на id или rn, поскольку я использую это для пакетного экспорта данных. Я приму свой ответ, чтобы он ускорялся, поскольку, как я думаю, другие ответы не подходят для больших таблиц. – tdma

1

Вы хотите получить запрос для первых 1000 строк, затем один для следующих 1000 и т. Д.?

Обычно вы просто пишете один запрос (тот, который вы уже используете), попросите приложение извлечь 1000 записей, сделайте с ними что-то, затем заберите следующие 1000 и так далее. Следовательно, нет необходимости в отдельных запросах.

Однако, было бы довольно легко писать такие частичные запросы:

select * 
from 
(
    SELECT id, row_number() over (ORDER BY id desc) AS rn, title 
    FROM mytable 
    WHERE group_id = 10 
) numbered 
where rn between 1 and 1000; -- <- simply change the row number range here 
          -- e.g. where rn between 1001 and 2000 for the second chunk 
+0

Я понимаю концепцию дб курсоров, как вы предлагаете, но, к сожалению, иногда это не представляется возможным использовать их. Ваш ответ работает, но очень медленный на больших таблицах из-за окончательного фильтра «между x и y», который не индексируется: я обнаружил, что pgsql выполняет последовательную фильтрацию по всему подзапросу, чтобы получить окончательную разбивку на страницы, которая занимает до 30 секунд на моей таблице :( – tdma

+0

Однако вы пишете запрос, СУБД придется сортировать всю таблицу, чтобы узнать, какие строки могут быть от 5001 до 6000, а затем перейти к этому фрагменту. Я не вижу реальной альтернативы. (Предложение Madhivanan использовать «LIMIT» и «OFFSET» - не плохая идея, но должно привести к тому же, упорядочить записи в подзапросе, перейти к фрагменту, а затем дать row_numbers для этих строк.) –

+1

OFFSET is не подходит для разбиения на несколько миллионов строк. – tdma

1

Вам нужна пагинация. Попробуйте

SELECT id, row_number() over (ORDER BY id desc)+0 AS row_number, title 
FROM mytable 
WHERE group_id = 10 AND id >= 0 AND id < 1000 
ORDER BY id ASC; 

В следующий раз, когда вы меняете начальное значение идентификатора в WHERE изменения клауза его в row_number(), а также, как показано ниже

SELECT id, row_number() over (ORDER BY id desc)+1000 AS row_number, title 
FROM mytable 
WHERE group_id = 10 AND id >= 1000 AND id < 2000 
ORDER BY id ASC; 

или лучше вы можете использовать OFFSET и LIMIT подход для пагинацией https://wiki.postgresql.org/images/3/35/Pagination_Done_the_PostgreSQL_Way.pdf

+0

Но тогда, делая это +1000, вы предполагаете, что каждый фрагмент/страница имеет ровно 1000 результатов, что может быть не так, из-за пробелов или, может быть, строк, не удовлетворяющих group_id = 10 , не так ли? – tdma

+0

Тогда посмотрим на ссылку, которую я разместил – Madhivanan

0

В конце концов, я в конечном итоге делает это таким образом:

Сначала я создать временный материализованное представление:

CREATE MATERIALIZED VIEW vw_temp AS SELECT id, row_number() over (ORDER BY id desc) AS rn, title 
FROM mytable 
WHERE group_id = 10; 

Тогда я определить индекс:

CREATE INDEX idx_temp ON vw_temp USING btree(id); 

Теперь я могу выполнять все операции очень быстро, и с пронумерованными строками:

SELECT * FROM vw_temp WHERE id BETWEEN 1000 AND 2000; 

После выполнения операций очистки:

DROP INDEX idx_temp; 
DROP MATERIALIZED VIEW vw_temp; 

Хотя Thorst Ответ Кеттнера кажется самым чистым, это было непрактично для меня из-за слишком медленного. Спасибо, что помогли всем. Для тех, кто интересуется практическим случаем, я использую это для подачи данных индексу Sphinx.