Aby rozwinąć odpowiedź na @ alci:
PostgreSQL nie obchodzi, w jakiej kolejności piszesz rzeczy
PostgreSQL nie przejmuje się w ogóle kolejnością wpisów w WHERE
klauzuli i wybiera indeksy oraz kolejność wykonania na podstawie samego oszacowania kosztów i selektywności.
Kolejność zapisywania złączeń jest również ignorowana aż do skonfigurowanej join_collapse_limit
; jeśli jest więcej sprzężeń, wykona je w kolejności, w jakiej zostały zapisane.
Podzapytania mogą być wykonywane przed lub po zapytaniu, które je zawiera, w zależności od tego, co jest najszybsze, pod warunkiem, że podzapytanie jest wykonywane, zanim zapytanie zewnętrzne faktycznie potrzebuje informacji. Często w rzeczywistości podzapytanie jest wykonywane w środku lub przeplatane z zewnętrznym zapytaniem.
Nie ma gwarancji, że PostgreSQL w ogóle wykona części zapytania. Można je całkowicie zoptymalizować. Jest to ważne, jeśli wywołujesz funkcje z efektami ubocznymi.
PostgreSQL przekształci twoje zapytanie
PostgreSQL mocno przekształci zapytania, zachowując dokładnie te same efekty, aby przyspieszyć ich działanie bez zmiany wyników.
Warunki poza podzapytaniem mogą zostać zepchnięte do podzapytania, dzięki czemu są wykonywane w ramach podzapytania, a nie tam, gdzie zostały zapisane w zapytaniu zewnętrznym
Warunki w podzapytaniu można wyciągnąć do zapytania zewnętrznego, aby ich wykonanie odbywało się w ramach zapytania zewnętrznego, a nie tam, gdzie je napisano w podzapytaniu
Podzapytanie może i jest często spłaszczone w złączenie na zewnętrznym stole. To samo dotyczy rzeczy takich jak zapytania EXISTS
i NOT EXISTS
zapytania.
Widoki zostają spłaszczone w zapytaniu, które korzysta z widoku
Funkcje SQL często są wprowadzane do zapytania wywołującego
... i do zapytań wprowadzono wiele innych transformacji, takich jak ciągła wstępna ocena wyrażeń, dekorelacja niektórych podkwerend i wszelkiego rodzaju inne sztuczki planowania / optymalizacji.
Ogólnie PostgreSQL może masowo przekształcić i przepisać zapytanie, do momentu, w którym każde z tych zapytań:
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
);
zazwyczaj wszystkie generują dokładnie ten sam plan zapytań. (Zakładając, że i tak nie popełniłem żadnych głupich błędów).
Często zdarza się, że próbujesz zoptymalizować zapytanie, ale okazuje się, że planista zapytań już wymyślił sztuczki, które próbujesz, i zastosował je automatycznie, więc wersja zoptymalizowana ręcznie nie jest lepsza od oryginalnej.
Ograniczenia
Planista / optymalizator nie jest wszechobecny i jest ograniczony wymogiem absolutnej pewności, że nie może zmienić efektów zapytania, dostępnych danych do podjęcia decyzji, wdrożonych reguł i czasu procesora stać go na zastanawianie się nad optymalizacjami. Na przykład:
Planista opiera się na statystykach prowadzonych przez ANALYZE
(zwykle poprzez autovacuum). Jeśli są nieaktualne, wybór planu może być zły.
Statystyki są tylko próbką, więc mogą wprowadzać w błąd ze względu na efekty próbkowania, zwłaszcza jeśli pobierana jest zbyt mała próbka. Może to spowodować złe wybory planu.
Statystyki nie śledzą niektórych rodzajów danych o tabeli, takich jak korelacje między kolumnami. Może to prowadzić planistę do podejmowania złych decyzji, gdy zakłada, że rzeczy są niezależne, a nie są.
Planista polega na parametrach kosztów, takich jak random_page_cost
określenie względnej prędkości różnych operacji w konkretnym systemie, na którym jest zainstalowany. To są tylko przewodniki. Jeśli są bardzo w błędzie, mogą prowadzić do złych wyborów planu.
Wszelkie podzapytania z LIMIT
lub OFFSET
nie mogą być spłaszczone ani podlegać pullup / pushdown. To nie znaczy, będzie to wykonać przed wszystkimi częściami zewnętrznej kwerendy, chociaż, albo nawet, że będzie to wykonać w ogóle .
Warunki CTE (klauzule w WITH
zapytaniu) są zawsze wykonywane w całości, jeśli w ogóle są wykonywane. Nie można ich spłaszczyć, a terminów nie można przesuwać w górę ani w dół przez barierę terminów CTE. Warunki CTE są zawsze wykonywane przed ostatnim zapytaniem. Jest to zachowanie niestandardowe dla SQL , ale jest udokumentowane jako działanie PostgreSQL.
PostgreSQL ma ograniczoną zdolność do optymalizacji zapytań dotyczących obcych tabel, security_barrier
widoków i niektórych innych specjalnych rodzajów relacji
PostgreSQL nie wstawi funkcji napisanej w niczym innym niż zwykły SQL, ani też pullup / pushdown między nią a zewnętrznym zapytaniem.
Planista / optymalizator jest naprawdę głupi w kwestii wybierania indeksów wyrażeń i trywialnych różnic typów danych między indeksem a wyrażeniami.
Mnóstwo też.
Twoje zapytanie
W przypadku zapytania:
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')
)
nic nie powstrzymuje go przed spłaszczeniem w prostsze zapytanie z dodatkowym zestawem sprzężeń, i najprawdopodobniej tak będzie.
Prawdopodobnie okaże się coś takiego (oczywiście nie przetestowane):
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 zoptymalizuje następnie kolejność łączenia i metody łączenia na podstawie swojej selektywności i oszacowań liczby wierszy oraz dostępnych indeksów. Jeśli te w rozsądny sposób odzwierciedlają rzeczywistość, dokona łączenia i uruchomi wpisy klauzuli where w dowolnej kolejności - często mieszając je razem, więc robi to trochę, potem trochę, a następnie wraca do pierwszej części itd.
Jak zobaczyć, co zrobił optymalizator
Nie można zobaczyć kodu SQL, do którego PostgreSQL optymalizuje zapytanie, ponieważ konwertuje ono SQL na wewnętrzną reprezentację drzewa zapytań, a następnie je modyfikuje. Państwo może zrzucić planu kwerend i porównać go z innymi zapytaniami.
Nie ma sposobu, aby „usunąć” ten plan zapytań lub wewnętrzne drzewo planu z powrotem do SQL.
http://explain.depesz.com/ ma pomocnika porządnego planu zapytań. Jeśli jesteś zupełnie nowy w wyszukiwaniu planów itp. (W tym przypadku jestem zaskoczony, że dotarłeś tak daleko w tym poście), to PgAdmin ma graficzną przeglądarkę planów zapytań, która zapewnia znacznie mniej informacji, ale jest prostsza.
Powiązana lektura:
Funkcje wypychania / podciągania i spłaszczania stale się poprawiają w każdym wydaniu . PostgreSQL zwykle ma rację co do decyzji podciągania / pchania / spłaszczania, ale nie zawsze, więc czasami musisz (ab) użyć CTE lub OFFSET 0
hacka. Jeśli znajdziesz taki przypadek, zgłoś błąd w narzędziu do planowania zapytań.
Jeśli jesteś naprawdę bardzo zainteresowany, możesz również skorzystać z debug_print_plans
opcji, aby zobaczyć plan zapytań, ale obiecuję, że nie chcesz tego czytać. Naprawdę.