Вы добавили больше информации, но еще больше актуально. Точное определение таблицы и индекса, количество элементов, распределение данных, статистика размера строки, количество диапазонов в предикате, назначение таблицы, шаблоны записи, ... Оптимизация производительности требует всех входных данных, которые она может получить.
Выстрел в темный: с неперекрывающимися диапазонами, UNION ALL
запрос может обеспечить наилучшую производительность:
SELECT * FROM playground WHERE val BETWEEN 1 AND 5
UNION ALL
SELECT * FROM playground WHERE val BETWEEN 20 AND 25
UNION ALL
SELECT * FROM playground WHERE val BETWEEN 200 AND 400;
Мы знаем , что диапазоны не перекрываются, но Postgres нет, поэтому он должен выполнять дополнительную работу в ваших попытках. Этот запрос должен избегать как BitmapOr
первого, так и Nested Loop
второго плана. Просто выберите каждый диапазон и добавьте его к выводу. Результатом должен быть план, подобный следующему:
Append (cost=0.13..24.50 rows=3 width=40)
-> Index Scan using playground_val_idx on playground (cost=0.13..8.15 rows=1 width=40)
Index Cond: ((val >= 1) AND (val <= 5))
-> Index Scan using playground_val_idx on playground playground_1 (cost=0.13..8.15 rows=1 width=40)
Index Cond: ((val >= 20) AND (val <= 25))
-> Index Scan using playground_val_idx on playground playground_2 (cost=0.13..8.15 rows=1 width=40)
Index Cond: ((val >= 200) AND (val <= 400))
Plus, каждый суб-SELECT
будет основан на фактической статистике для данного диапазона, а не на общих c оценках, даже для более длинного списка диапазонов. Смотрите (рекомендуется!):
Вы можете сгенерировать запрос в своем клиенте или написать серверная функция для генерации и выполнения динамических c SQL (применимо, так как тип результата известен).
Вы можете даже протестировать серверную функцию, используя LOOP
(который часто менее эффективно, но это может быть исключением):
CREATE OR REPLACE FUNCTION foo(_ranges int[])
RETURNS SETOF playground LANGUAGE plpgsql PARALLEL SAFE STABLE AS
$func$
DECLARE
_range int[];
BEGIN
FOREACH _range SLICE 1 IN ARRAY _ranges
LOOP
RETURN QUERY
SELECT * FROM playground WHERE val BETWEEN _range[1] AND _range[2];
END LOOP;
END
$func$;
Служебные расходы могут не оплачивать несколько диапазонов в вызове. Но очень удобно вызывать, если ничего больше:
SELECT * FROM foo('{{1,5},{20,25},{200,400}}');
Связанный:
дБ <> скрипка здесь
Физический порядок строк может помочь много, Если строки хранятся в последовательности, (намного) нужно обрабатывать меньше страниц данных. Зависит от нераскрытых деталей. В этом могут помочь встроенные CLUSTER
или расширения pg_repack
или pg_squeeze
. Связанные:
И рекомендуется использовать последний доступный вспомогательный выпуск для какая бы основная версия не использовалась. На момент написания статьи было бы 12.2 (выпущено 2020-02-13).