я в настоящее время работаю над сложной проблемой сортировки в Postgres 9.2 Вы можете найти исходный код, используемые в этом вопросе (упрощенном) здесь: http://sqlfiddle.com/#!12/9857e/11Postgresql Сортировка присоединяемой таблицы с индексом
У меня есть огромный (>> 20Mio rows), содержащую различные столбцы разных типов.
CREATE TABLE data_table
(
id bigserial PRIMARY KEY,
column_a character(1),
column_b integer
-- ~100 more columns
);
Допустим, я хочу рода эту таблицу более 2 Столбцы (ASC). Но я не хочу делать это с помощью простого порядка By, потому что позже мне может понадобиться вставить строки в отсортированный результат, и пользователь, вероятно, только хочет видеть 100 строк сразу (из отсортированного вывода) ,
Для достижения этих целей я сделать следующее:
CREATE TABLE meta_table
(
id bigserial PRIMARY KEY,
id_data bigint NOT NULL -- refers to the data_table
);
--Function to get the Column A of the current row
CREATE OR REPLACE FUNCTION get_column_a(bigint)
RETURNS character AS
'SELECT column_a FROM data_table WHERE id=$1'
LANGUAGE sql IMMUTABLE STRICT;
--Function to get the Column B of the current row
CREATE OR REPLACE FUNCTION get_column_b(bigint)
RETURNS integer AS
'SELECT column_b FROM data_table WHERE id=$1'
LANGUAGE sql IMMUTABLE STRICT;
--Creating a index on expression:
CREATE INDEX meta_sort_index
ON meta_table
USING btree
(get_column_a(id_data), get_column_b(id_data), id_data);
А потом скопировать корочки data_table к meta_table:
INSERT INTO meta_table(id_data) (SELECT id FROM data_table);
Позже можно добавить дополнительные строки в таблицу с аналогичная простая вставка.
Чтобы получить Ряды 900000 - 900099 (100 строк) Теперь я могу использовать:
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
ORDER BY 1,2,3 OFFSET 900000 LIMIT 100;
(. С дополнительным INNER JOIN на data_table, если я хочу, чтобы все данные)
Результирующий План:
Limit (cost=498956.59..499012.03 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.00..554396.21 rows=1000000 width=8)
Это довольно эффективный план (только для сканирования только в Postgres 9.2).
Но что, если я хочу получить строки 20'000'000 - 20'000'099 (100 строк)? Тот же план, гораздо более длительное время исполнения. Ну, чтобы улучшить производительность смещения (Improving OFFSET performance in PostgreSQL), я могу сделать следующее (предположим, я сохранил каждые 100'000 рядов в другую таблицу).
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
WHERE (get_column_a(id_data), get_column_b(id_data), id_data) >= (get_column_a(587857), get_column_b(587857), 587857)
ORDER BY 1,2,3 LIMIT 100;
Это работает намного быстрее. Результирующий план:
Limit (cost=0.51..61.13 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.51..193379.65 rows=318954 width=8)
Index Cond: (ROW((get_column_a(id_data)), (get_column_b(id_data)), id_data) >= ROW('Z'::bpchar, 27857, 587857))
До сих пор все работает отлично, и postgres отлично справляется!
Предположим, я хочу изменить порядок 2-й колонны на DESC.
Но тогда мне пришлось бы изменить свое предложение WHERE, потому что оператор> Оператор сравнивает обе колонки ASC. Тот же самый запрос, как описаны выше (ASC Упорядочение) также можно записать в виде:
SELECT get_column_a(id_data), get_column_b(id_data), id_data
FROM meta_table
WHERE
(get_column_a(id_data) > get_column_a(587857))
OR (get_column_a(id_data) = get_column_a(587857) AND ((get_column_b(id_data) > get_column_b(587857))
OR ( (get_column_b(id_data) = get_column_b(587857)) AND (id_data >= 587857))))
ORDER BY 1,2,3 LIMIT 100;
Теперь план изменения и запросы становятся медленными:
Limit (cost=0.00..1095.94 rows=100 width=8)
-> Index Only Scan using meta_sort_index on meta_table (cost=0.00..1117877.41 rows=102002 width=8)
Filter: (((get_column_a(id_data)) > 'Z'::bpchar) OR (((get_column_a(id_data)) = 'Z'::bpchar) AND (((get_column_b(id_data)) > 27857) OR (((get_column_b(id_data)) = 27857) AND (id_data >= 587857)))))
Как я могу использовать эффективный старший план с DESC- Заказ?
Есть ли у вас лучшие идеи, как решить проблему?
(я уже пытался объявить собственный тип с собственными классами оператора, но это слишком медленно)
Благодаря http://stackoverflow.com/questions/1677538/advanced-indexing-involving-or-ed-conditions-pgsql Я попробовал UNIONS. Это немного лучше, чем последний план, но этого все еще недостаточно. http://sqlfiddle.com/#!12/9857e/28/3 – Dreamcooled