Детальніше про відповідь @ 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 завжди виконуються перед остаточним запитом. Це не стандартне поведінка SQL , але це документально підтверджено як PostgreSQL робить справи.
PostgreSQL має обмежену здатність оптимізувати запити на іноземних таблицях, security_barrier
представленнях та деяких інших спеціальних відносинах
PostgreSQL не буде вбудовувати функцію, записану в чому-небудь, крім звичайного SQL, і не здійснювати підтягування / віджимання між ним та зовнішнім запитом.
Планувальник / оптимізатор дійсно немічний щодо вибору індексів вираження та щодо тривіальних відмінностей між типом даних та виразом.
Тонів більше.
Ваш запит
У випадку вашого запиту:
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 оптимізує порядок з'єднання та методи з'єднання на основі його вибірковості та оцінки кількості рядків та доступних індексів. Якщо ці обгрунтовано відображають реальність, то вони зроблять об'єднання та запускатимуть записи пункту де в будь-якому порядку найкраще - часто змішуючи їх разом, так що це робить трохи цього, то трохи цього, потім повертається до першої частини тощо.
Як побачити, що зробив оптимізатор
Ви не можете бачити SQL, за допомогою якого PostgreSQL оптимізує ваш запит, оскільки він перетворює SQL у внутрішнє представлення дерева запитів, а потім змінює його. Ви можете скинути план запитів і порівняти його з іншими запитами.
Немає способу повернути цей план запитів чи внутрішнє дерево плану назад до SQL.
http://explain.depesz.com/ має гідний помічник плану запитів. Якщо ви абсолютно новачок у планах запитів тощо (у такому випадку я вражений, ви зробили це далеко через цю посаду), тоді PgAdmin має графічний переглядач плану запитів, який надає набагато менше інформації, але простіший.
Пов'язане читання:
Можливості віджимання / підтягування та вирівнювання продовжують удосконалюватися в кожному випуску . PostgreSQL, як правило, стосується рішень про підтягування / натискання / вирівнювання, але не завжди, тому періодично доводиться (ab) використовувати CTE або OFFSET 0
хак. Якщо ви виявили такий випадок, повідомте про помилку в планувальнику запитів.
Якщо ви насправді дуже зацікавлені, ви також можете скористатися debug_print_plans
опцією, щоб переглянути план запитів, але я обіцяю, що ви не хочете цього читати. Дійсно.