Pour élaborer sur la réponse de @ alci:
PostgreSQL ne se soucie pas de l'ordre dans lequel vous écrivez
PostgreSQL ne se soucie pas du tout de l'ordre des entrées dans une WHERE
clause et choisit les index et l'ordre d'exécution en fonction de l'estimation du coût et de la sélectivité uniquement.
L'ordre dans lequel les jointures sont écrites est également ignoré jusqu'à la configuration join_collapse_limit
; s'il y a plus de jointures que cela, elles seront exécutées dans l'ordre dans lequel elles ont été écrites.
Les sous-requêtes peuvent être exécutées avant ou après la requête qui les contient, en fonction de ce qui est le plus rapide, à condition que la sous-requête soit exécutée avant que la requête externe ait réellement besoin des informations. Souvent, en réalité, la sous-requête est exécutée au milieu ou est entrelacée avec la requête externe.
Il n'y a aucune garantie que PostgreSQL exécute réellement une partie de la requête. Ils peuvent être complètement optimisés. Ceci est important si vous appelez des fonctions avec des effets secondaires.
PostgreSQL va transformer votre requête
PostgreSQL va considérablement transformer les requêtes tout en conservant exactement les mêmes effets, afin de les rendre plus rapides sans modifier les résultats.
Les termes extérieurs à une sous-requête peuvent être insérés dans la sous-requête afin qu'ils s'exécutent dans le cadre de la sous-requête et non à l'endroit où vous les avez écrits dans la requête externe.
Les termes de la sous-requête peuvent être extraits de la requête externe afin que leur exécution soit effectuée dans le cadre de la requête externe et non à l'endroit où vous les avez écrits dans la sous-requête.
La sous-requête peut, et est souvent, aplatie dans une jointure sur la table externe. La même chose est vraie des choses comme EXISTS
et des NOT EXISTS
requêtes.
Les vues sont aplaties dans la requête utilisant la vue
Les fonctions SQL sont souvent intégrées à la requête appelante
... et de nombreuses autres transformations ont été apportées aux requêtes, telles que la pré-évaluation de l'expression constante, la décorrélation de certaines sous-requêtes et toutes sortes d'autres astuces de planification / optimisation.
En général, PostgreSQL peut transformer et réécrire votre requête de manière massive, au point où chacune de ces requêtes:
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
);
Généralement, tous produiront exactement le même plan de requête. (En supposant que je n’ai fait aucune erreur stupide dans ce qui précède de toute façon).
Il n'est pas rare d'essayer d'optimiser une requête uniquement pour constater que le planificateur de requêtes a déjà compris les astuces que vous tentez et les a appliquées automatiquement. La version optimisée à la main n'est donc pas meilleure que la version d'origine.
Limites
Le planificateur / optimiseur est loin d'être omniprésent et est limité par l'obligation d'être absolument certain qu'il ne peut pas modifier les effets de la requête, les données disponibles pour prendre des décisions, les règles mises en œuvre et le temps de calcul il peut se permettre de réfléchir aux optimisations. Par exemple:
Le planificateur s'appuie sur des statistiques conservées par ANALYZE
(généralement via autovacuum). Si ceux-ci sont obsolètes, le choix du plan peut être mauvais.
Les statistiques ne sont qu'un échantillon, elles peuvent donc être trompeuses en raison des effets de l'échantillonnage, en particulier si un échantillon trop petit est pris. De mauvais choix de régime peuvent en résulter.
Les statistiques ne gardent pas trace de certains types de données sur la table, telles que les corrélations entre les colonnes. Cela peut amener le planificateur à prendre de mauvaises décisions lorsqu'il suppose que les choses sont indépendantes alors qu'elles ne le sont pas.
Le planificateur s'appuie sur des paramètres de coût, comme random_page_cost
pour lui indiquer la vitesse relative des différentes opérations sur le système sur lequel il est installé. Ce ne sont que des guides. S'ils ont tort, ils peuvent mener à de mauvais choix de régimes.
Toute sous-requête avec un LIMIT
ou OFFSET
ne peut pas être aplatie ou être sujette à un pull-up / push-down. Cela ne signifie pas qu'il s'exécutera avant toutes les parties de la requête externe, ni même qu'il s'exécutera du tout .
Les termes CTE (les clauses d'une WITH
requête) sont toujours exécutés dans leur intégralité, s'ils sont exécutés du tout. Ils ne peuvent pas être aplatis, et les termes ne peuvent pas être augmentés ou abaissés au-delà de la barrière des termes CTE. Les termes CTE sont toujours exécutés avant la requête finale. Ceci est un comportement non standard SQL , mais il est documenté comment PostgreSQL fait les choses.
PostgreSQL a une capacité limitée à optimiser des requêtes sur des tables étrangères, des security_barrier
vues et certains autres types de relations spéciales.
PostgreSQL ™ ne met pas en ligne une fonction écrite dans un langage autre que du SQL simple, pas plus qu’un pull-up / pushdown entre celle-ci et la requête externe.
Le planificateur / optimiseur est vraiment stupide en ce qui concerne la sélection d'index d'expression et les différences de type de données triviales entre index et expression.
Des tonnes plus, aussi.
Votre requête
Dans le cas de votre requête:
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')
)
rien ne l'empêche d'être transformée en une requête plus simple avec un ensemble supplémentaire de jointures et ce sera très probablement le cas.
Cela donnera probablement quelque chose comme (non testé, évidemment):
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 va ensuite optimiser l'ordre et les méthodes de jointure en fonction de ses estimations de la sélectivité et du nombre de lignes et des index disponibles. Si ceux-ci reflètent raisonnablement la réalité, les jointures sont exécutées et les entrées de la clause where sont exécutées dans l'ordre qui leur convient le mieux, en les mélangeant souvent. , etc.
Comment voir ce que l'optimiseur a fait
Vous ne pouvez pas voir le SQL dans lequel PostgreSQL optimise votre requête, car il convertit le SQL en une représentation d'arborescence de requête interne, puis le modifie. Vous pouvez vider le plan de requête et le comparer à d'autres requêtes.
Il n'y a aucun moyen de "déparper" ce plan de requête ou l'arborescence de plan interne en SQL.
http://explain.depesz.com/ dispose d'un assistant de plan de requête décent. Si vous êtes totalement nouveau en matière de plans de requête, etc. (dans ce cas, je suis étonné que vous ayez réussi à le faire jusqu'à présent dans cet article), alors PgAdmin propose un visualiseur graphique de plan de requête qui fournit beaucoup moins d'informations mais est plus simple.
Lecture connexe:
Les capacités de compression et de compression et d'aplatissement continuent de s'améliorer dans chaque version . PostgreSQL a généralement raison sur les décisions d'abaissement / d'abaissement / d'aplatissement, mais pas toujours. Vous devez donc parfois utiliser un CTE ou un OFFSET 0
hack. Si vous trouvez un tel cas, signalez un bogue du planificateur de requêtes.
Si vous êtes vraiment très enthousiaste, vous pouvez également utiliser l' debug_print_plans
option pour voir le plan de requête brut, mais je vous promets que vous ne voulez pas lire cela. Vraiment.