Чтобы уточнить ответ @ alci:
PostgreSQL не волнует, в каком порядке вы пишете
PostgreSQL совершенно не заботится о порядке записей в WHERE
предложении и выбирает индексы и порядок выполнения, основываясь только на оценке стоимости и селективности.
Порядок, в котором записываются соединения, также игнорируется до настроенного join_collapse_limit
; если есть больше соединений, чем это, он выполнит их в порядке их написания.
Подзапросы могут выполняться до или после запроса, который их содержит, в зависимости от того, что является самым быстрым, при условии, что подзапрос выполняется до того, как внешний запрос действительно нуждается в информации. Часто в действительности подзапрос выполняется как бы посередине или чередуется с внешним запросом.
Нет никаких гарантий, что PostgreSQL вообще выполнит части запроса. Они могут быть полностью оптимизированы. Это важно, если вы вызываете функции с побочными эффектами.
PostgreSQL преобразит ваш запрос
PostgreSQL будет сильно преобразовывать запросы, сохраняя те же самые эффекты, чтобы они выполнялись быстрее, не меняя результаты.
Термины вне подзапроса могут быть помещены в подзапрос, поэтому они выполняются как часть подзапроса, а не там, где вы написали их во внешнем запросе.
Термины в подзапросе могут быть перенесены во внешний запрос, поэтому их выполнение выполняется как часть внешнего запроса, а не там, где вы написали их в подзапросе.
Подзапрос может и часто сводится в соединение на внешней таблице. То же самое можно сказать и о таких вещах , как EXISTS
и NOT EXISTS
запросы.
Представления сведены в запрос, который использует представление
Функции SQL часто вставляются в вызывающий запрос
... и есть множество других преобразований, сделанных в запросах, таких как предварительная оценка константных выражений, декорреляция некоторых подзапросов и всевозможные другие приемы планирования / оптимизации.
В общем, PostgreSQL может массово преобразовать и переписать ваш запрос до точки, где каждый из этих запросов:
select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;
select *
from my_table
where not exists (
select 1
from other_table
where other_table.my_table_id = my_table.id
);
select *
from my_table
where my_table.id not in (
select my_table_id
from other_table
where my_table_id is not null
);
обычно все выдают точно такой же план запроса. (Предполагая, что в любом случае я не допустил никаких глупых ошибок).
Нет ничего необычного в том, чтобы попытаться оптимизировать запрос только для того, чтобы выяснить, что планировщик запросов уже определил уловки, которые вы пытаетесь, и применил их автоматически, поэтому оптимизированная вручную версия не лучше оригинальной.
Ограничения
Планировщик / оптимизатор далеко не универсален и ограничен требованием быть абсолютно уверенным в том, что он не может изменить эффекты запроса, доступные данные для принятия решений, реализованные правила и время ЦП. это может позволить себе потратить на обдумывание оптимизаций. Например:
Планировщик полагается на сохраняемую статистику ANALYZE
(обычно через автовакуум). Если они устарели, выбор плана может быть плохим.
Статистические данные являются только выборкой, поэтому они могут вводить в заблуждение из-за эффектов выборки, особенно если выборка слишком мала. Неудачный выбор плана может привести к.
Статистика не отслеживает некоторые виды данных о таблице, такие как корреляции между столбцами. Это может привести к тому, что планировщик будет принимать неправильные решения, когда он предполагает, что вещи независимы, а они - нет.
Планировщик полагается на параметры стоимости, такие как random_page_cost
указание относительной скорости различных операций в конкретной системе, на которой он установлен. Это только руководства. Если они сильно ошибаются, они могут привести к неправильному выбору плана.
Любой подзапрос с LIMIT
или OFFSET
не может быть сплющен или подвергаться подтягиванию / опусканию. Это не означает, что он будет выполняться раньше всех частей внешнего запроса, или даже что он будет выполнен вообще .
Термины CTE (предложения в WITH
запросе) всегда выполняются полностью, если они выполняются вообще. Они не могут быть сплющены, и термины не могут быть сдвинуты вверх или вниз через терминологический барьер CTE. Термины CTE всегда выполняются перед окончательным запросом. Это нестандартное поведение, но задокументировано, как PostgreSQL работает.
PostgreSQL имеет ограниченную способность оптимизировать запросы к внешним таблицам, security_barrier
представлениям и некоторым другим специальным типам отношений.
PostgreSQL не встроит функцию, написанную на чем-либо, кроме простого SQL, и не будет выполнять pullup / pushdown между ней и внешним запросом.
Планировщик / оптимизатор действительно глуп в выборе индексов выражений и в тривиальных различиях типов данных между индексом и выражением.
Тонны больше тоже.
Ваш запрос
В случае вашего запроса:
select 1
from workdays day
where day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30'
and day.offer_id in (
select offer.offer_day
from offer
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
)
ничто не мешает превратить его в более простой запрос с дополнительным набором объединений, и, скорее всего, так и будет.
Вероятно, получится что-то вроде (не проверено, очевидно):
select 1
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
and day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30';
Затем PostgreSQL оптимизирует порядок соединения и методы соединения, основываясь на его селективности и оценках количества строк и доступных индексах. Если они разумно отражают реальность, тогда они выполнят объединения и выполнят записи в предложении where в любом порядке - часто смешивая их вместе, поэтому немного, а потом немного возвращается к первой части. , так далее.
Как посмотреть, что сделал оптимизатор
Вы не можете видеть SQL, в который PostgreSQL оптимизирует ваш запрос, потому что он преобразует SQL во внутреннее представление дерева запросов, а затем изменяет его. Вы можете сбросить план запроса и сравнить его с другими запросами.
Нет никакого способа «отменить» этот план запроса или внутреннее дерево плана до SQL.
У http://explain.depesz.com/ есть достойный помощник по плану запросов. Если вы совершенно не знакомы с планами запросов и т. Д. (В этом случае я удивлен, что вы сделали это так далеко через этот пост), тогда PgAdmin имеет графическое средство просмотра планов запросов, которое предоставляет гораздо меньше информации, но проще.
Связанное чтение:
Pushdown / подтягивающие и уплощение возможности продолжать улучшаться в каждом выпуске . PostgreSQL обычно прав насчет решений подтягивания / опускания / сплющивания, но не всегда, поэтому иногда приходится (ab) использовать CTE или OFFSET 0
взламывать. Если вы обнаружите такой случай, сообщите об ошибке планировщика запросов.
Если вы действительно, очень заинтересованы, вы также можете использовать debug_print_plans
опцию, чтобы увидеть план необработанных запросов, но я обещаю, что вы не хотите читать это. В самом деле.