У меня есть запрос, скажем, X_QUERY
, этот запрос может быть любым (мы не знаем ни одного поля) и может иметь сто миллионов строк. Мне нужно разделить результат на 16 фрагментов, чтобы каждый фрагмент имел совершенно разные строки X_QUERY
. Мы могли бы предположить, что во время процесса нет обновлений ни в одной таблице.
Я решил ту же проблему в оракуле, используя ROWID
как предложение order by
, поэтому я попытался использовать CTID
в Postgres, но не сработал.
SELECT *
FROM (X_QUERY) as origin ORDER BY ctid -- <-- ctid does not exist here
limit 6250000 offset 0; -- <-- next offsets should be 6250000, 12500000 etc.
В идеале хотелось бы избежать дополнительных затрат на order by
, но другого пути (по крайней мере, с помощью Oracle) я не нашел.
Итак, есть ли способ избежать какого-то порядка?
Если нет, есть ли способ каждый раз выбирать разные строки, не зная ни одного поля для заказа?
Спасибо, но я так понимаю, что эти подходы не решают основной проблемы.
Вы можете добавить оконную функцию в X_QUERY с именем row_number().
Единственный возможный способ, который я вижу, - это использовать row_number() over(), и это не гарантирует один и тот же фрагмент каждый раз, когда мы запускаем один и тот же запрос, поэтому фрагменты не могут быть гарантированно отличными друг от друга.
Хм. В вашем первом посте этой информации не было. Итак, строки X_QUERY не уникальны?
Это один из редких случаев, когда я бы обратился к курсору SQL. Мануал по DECLARE:
DECLARE
позволяет пользователю создавать курсоры, которые можно использовать для одновременного извлечения небольшого количества строк из более крупного запроса.
Он не заботится о базовом порядке сортировки и возвращает строки в той последовательности, в которой его создал запрос.
Используйте FETCH, чтобы получить следующий набор строк.
Пример:
BEGIN;
DECLARE x_cursor CURSOR FOR <X_QUERY>; -- your query string here
FETCH 6250000 FROM x_cursor;
FETCH 6250000 FROM x_cursor;
FETCH 6250000 FROM x_cursor;
-- repeat until no more rows;
-- CLOSE x_cursor; -- optional
COMMIT;
-- or ROLLBACK; -- does not make a difference in this case
Все должно быть сделано в рамках транзакции, если вы не объявили корсор WITH HOLD
. Инструкция:
Если не указано
WITH HOLD
, курсор, созданный этой командой можно использовать только в рамках текущей транзакции. Таким образом,DECLARE
безWITH HOLD
бесполезен вне блока транзакции: курсор будет существовать только до завершения оператора. Поэтому PostgreSQL сообщает об ошибке, если такая команда используется за пределами блок транзакций. ИспользуйтеBEGIN
иCOMMIT
(илиROLLBACK
), чтобы определить блок транзакций.
Если у вас есть запрос в виде строки, вы можете использовать динамический SQL в функции PL/pgSQL или оператор DO
для динамического создания курсора SQL (WITH HOLD
?) или использовать курсор PL/pgSQL для начала (связанный, но отдельная реализация).
Я пытался использовать CTID в postgres, но не работал.
Это потому, что ctid
— это системный столбец, определяющий физическое расположение кортежа. Он не включается в результат запроса, если он явно не указан в списке SELECT
. Таким образом, обычно это не результат данного запроса, и он не обязательно уникален в этой производной таблице. Следовательно, ctid
можно использовать для обхода таблицы без одновременной записи, но это не подходит для ваших целей.
Более кровавые подробности:
Спасибо, отличное объяснение.
Пагинация в postgresql: citusdata.com/blog/2016/03/30/five-ways-to-paginate