Я уже спрашивал, почему столбец не смещается вниз и оптимизация не применяется.
И получите подробное объяснение в список рассылки
Были проблемы, когда я использовал whole-row
vars. Но здесь я их не использую.
Запрос работает медленно, когда я делаю LEFT JOIN
или помещаю этот LEFT JOIN
внутрь CTE
.
Когда я запускаю запрос напрямую (или помещаю условие внутри), для запуска требуется всего 6 мс:
WITH ready AS (
SELECT
agreement_id, sys_period, app_period,
acc_i.ready as acc_i_ready,
acc_i.acc_period as acc_i_period,
acc_i.consumed_period as acc_i_consumed_period,
acc_u.ready as acc_u_ready,
acc_u.acc_period as acc_u_period,
acc_u.consumed_period as acc_u_consumed_period
FROM "order_bt" o
LEFT JOIN acc_ready( 'Invoice', app_period(), o ) acc_i ON acc_i.ready
LEFT JOIN acc_ready( 'Usage', app_period(), o ) acc_u ON acc_u.ready
WHERE o.sys_period @> sys_time() AND o.app_period && app_period()
AND agreement_id = 1736
)
SELECT *
FROM ready
Когда я просто повторяю условие снаружи, это занимает 45 мс
WITH ready AS (
SELECT
agreement_id, sys_period, app_period,
acc_i.ready as acc_i_ready,
acc_i.acc_period as acc_i_period,
acc_i.consumed_period as acc_i_consumed_period,
acc_u.ready as acc_u_ready,
acc_u.acc_period as acc_u_period,
acc_u.consumed_period as acc_u_consumed_period
FROM "order_bt" o
LEFT JOIN acc_ready( 'Invoice', app_period(), o ) acc_i ON acc_i.ready
LEFT JOIN acc_ready( 'Usage', app_period(), o ) acc_u ON acc_u.ready
WHERE o.sys_period @> sys_time() AND o.app_period && app_period()
AND agreement_id = 1736
)
SELECT *
FROM ready
-- NOTICE: I only repeat this!
WHERE sys_period @> sys_time() AND app_period && app_period()
AND agreement_id = 1736
Когда я разделяю условие, это занимает 671 мс:
WITH ready AS (
SELECT
agreement_id, sys_period, app_period,
acc_i.ready as acc_i_ready,
acc_i.acc_period as acc_i_period,
acc_i.consumed_period as acc_i_consumed_period,
acc_u.ready as acc_u_ready,
acc_u.acc_period as acc_u_period,
acc_u.consumed_period as acc_u_consumed_period
FROM "order_bt" o
LEFT JOIN acc_ready( 'Invoice', app_period(), o ) acc_i ON acc_i.ready
LEFT JOIN acc_ready( 'Usage', app_period(), o ) acc_u ON acc_u.ready
WHERE o.sys_period @> sys_time() AND o.app_period && app_period()
)
SELECT *
FROM ready
WHERE
agreement_id = 1736 -- Unfortunately this is not used for index scan =( Why???
Когда я перемещаю все условия наружу, это занимает уже 11084 мс:
(хммм, но я не нарушать встроенное условие, не так ли?)
WITH ready AS (
SELECT
agreement_id, sys_period, app_period,
acc_i.ready as acc_i_ready,
acc_i.acc_period as acc_i_period,
acc_i.consumed_period as acc_i_consumed_period,
acc_u.ready as acc_u_ready,
acc_u.acc_period as acc_u_period,
acc_u.consumed_period as acc_u_consumed_period
FROM "order_bt" o
LEFT JOIN acc_ready( 'Invoice', app_period(), o ) acc_i ON acc_i.ready
LEFT JOIN acc_ready( 'Usage', app_period(), o ) acc_u ON acc_u.ready
)
SELECT *
FROM ready
WHERE sys_period @> sys_time() AND app_period && app_period()
AND agreement_id = 1736
А когда я удаляю LEFT JOIN
, это занимает всего 5 мс!
WITH ready AS (
SELECT
agreement_id, sys_period, app_period
FROM "order_bt" o
)
SELECT *
FROM ready
WHERE sys_period @> sys_time() AND app_period && app_period()
AND agreement_id = 1736
Сравните: 6 мс VS 45 мс VS 671 мс VS 11084 мс VS 5 мс
То же самое на depesz: 6 мс VS 45 мс VS 671 мс VS 11084 мс VS 5 мс
Почему второй, третий и четвертый запросы не оптимизированы как первый/последний запросы?
Есть ли какие-то обходные пути для четвертого случая, чтобы он работал быстрее?
(например, выбрать дополнительные поля, которые описаны в списке рассылки ( смотри ссылки выше))
PS. Второй случай кажется мне нелогичным и не является ожидаемым поведением
Версия сервера 13.1.
*первый — когда условие находится только внутри CTE
*второй — когда это условие копируется за пределы CTE
UPD Дальнейшие расследования:
попробуйте I1
Здесь, когда функция volatile для первого и второго случаев, BitmapHeapScan
применяется по двум индексам:order_idx_agreement_id
, order_id_sys_period_app_period_excl
попробуйте I2
После замены функции acc_ready
на STABLE
,
для первого запроса используется BitmapHeapScan
.
для второго запроса используется IndexScan
(по ошибке?).
Сравните 7 мс VS 11 мс а>
Здесь мы видим, что встраивание экономит нам 2 мс
и 34 мс (из-за отсутствия JIT-генерации).
Но все равно требуется дополнительно 4 мс (по ошибке?), когда я повторяю условие снаружи.
(Здесь я не ожидаю дополнительных 4 мс времени, потому что внутри CTE условие не изменилось, поэтому изменений во времени тоже быть не должно, не так ли?)
попробуйте I3
Наконец, когда я удаляю индекс order_id_sys_period_app_period_excl
, снова используется BitmapHeapScan
(вместо более медленного IndexScan
).
Это самый быстрый запрос, который занимает всего 3 мс
Остаются открытыми вопросы:
Почему IndexScan
использовалось в разделе I2, а BitmapHeapScan
— в разделе I1?
Почему BitmapIndexScan
не применяются друг за другом, как это было сделано в разделе I3? (самый быстрый случай)