Para elaborar sobre la respuesta de @ alci:
A PostgreSQL no le importa en qué orden escribes las cosas
PostgreSQL no se preocupa en absoluto por el orden de las entradas en una WHERE
cláusula, y elige los índices y el orden de ejecución basados únicamente en la estimación de costos y selectividad.
El orden en que se escriben las uniones también se ignora hasta el configurado join_collapse_limit
; Si hay más combinaciones que eso, las ejecutará en el orden en que están escritas.
Las subconsultas se pueden ejecutar antes o después de la consulta que las contiene, dependiendo de lo que sea más rápido, siempre que la subconsulta se ejecute antes de que la consulta externa realmente necesite la información. A menudo, en realidad, la subconsulta se ejecuta en el medio o se intercala con la consulta externa.
No hay garantía de que PostgreSQL realmente ejecute partes de la consulta. Se pueden optimizar por completo. Esto es importante si llama a funciones con efectos secundarios.
PostgreSQL transformará su consulta
PostgreSQL transformará en gran medida las consultas al tiempo que conserva los mismos efectos exactos, para que se ejecuten más rápido sin cambiar los resultados.
Términos fuera una subconsulta puede conseguir empujado hacia abajo en el sub consulta para que ejecuten en el marco de la subconsulta no donde las escribió en la consulta externa
Los términos en la subconsulta se pueden extraer a la consulta externa para que su ejecución se realice como parte de la consulta externa, no donde los escribió en la subconsulta
La subconsulta puede, y a menudo es, aplanada en una unión en la mesa exterior. Lo mismo es cierto para cosas como EXISTS
y NOT EXISTS
consultas.
Las vistas se aplanan en la consulta que usa la vista
Las funciones SQL a menudo se insertan en la consulta de llamada
... y hay muchas otras transformaciones hechas a las consultas, como la evaluación previa de la expresión constante, la des-correlación de algunas subconsultas y todo tipo de otros trucos de planificador / optimizador.
En general, PostgreSQL puede transformar y reescribir masivamente su consulta, hasta el punto en que cada una de estas consultas:
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
);
por lo general, todos producirán exactamente el mismo plan de consulta. (Asumiendo que no cometí ningún error tonto en lo anterior de todos modos).
No es raro tratar de optimizar una consulta solo para descubrir que el planificador de consultas ya descubrió los trucos que está intentando y los aplicó automáticamente, por lo que la versión optimizada a mano no es mejor que la original.
Limitaciones
El planificador / optimizador está lejos de ser omnipresente, y está limitado por el requisito de estar absolutamente seguro de que no puede cambiar los efectos de la consulta, los datos disponibles para tomar decisiones, las reglas que se han implementado y el tiempo de CPU puede permitirse el lujo de reflexionar sobre las optimizaciones. Por ejemplo:
El planificador se basa en estadísticas mantenidas por ANALYZE
(generalmente a través de autovacuum). Si estos están desactualizados, la elección del plan puede ser mala.
Las estadísticas son solo una muestra, por lo que pueden ser engañosas debido a los efectos de muestreo, especialmente si se toma una muestra demasiado pequeña. Pueden resultar malas elecciones de plan.
Las estadísticas no realizan un seguimiento de algunos tipos de datos sobre la tabla, como las correlaciones entre columnas. Esto puede llevar al planificador a tomar malas decisiones cuando asume que las cosas son independientes cuando no lo son.
El planificador se basa en parámetros de costo como random_page_cost
decirle la velocidad relativa de varias operaciones en el sistema particular en el que está instalado. Estas son solo guías. Si están muy equivocados, pueden conducir a malas elecciones de planes.
Cualquier subconsulta con LIMIT
o OFFSET
no se puede aplanar o estar sujeta a pullup / pushdown. Sin embargo, esto no significa que se ejecutará antes de todas las partes de la consulta externa, o incluso que se ejecutará en absoluto .
Los términos CTE (las cláusulas en una WITH
consulta) siempre se ejecutan en su totalidad, si es que se ejecutan. No se pueden aplanar, y los términos no se pueden subir o bajar a través de la barrera del término CTE. Los términos CTE siempre se ejecutan antes de la consulta final. Este es un comportamiento no estándar de SQL , pero está documentado como PostgreSQL hace las cosas.
PostgreSQL tiene una capacidad limitada para optimizar las consultas en tablas externas, security_barrier
vistas y ciertos otros tipos especiales de relación
PostgreSQL no alineará una función escrita en nada excepto SQL simple, ni tampoco pullup / pushdown entre ella y la consulta externa.
El planificador / optimizador es realmente tonto sobre la selección de índices de expresión y sobre las diferencias triviales de tipos de datos entre índice y expresión.
Toneladas más, también.
Su consulta
En el caso de su consulta:
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')
)
nada impide que se aplaste en una consulta más simple con un conjunto adicional de combinaciones, y muy probablemente lo será.
Probablemente resulte algo como (sin probar, obviamente):
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 optimizará el orden de unión y los métodos de unión basados en su selectividad y estimaciones de recuento de filas y los índices disponibles. Si estos reflejan razonablemente la realidad, entonces hará las uniones y ejecutará las entradas de la cláusula where en el orden que sea mejor, a menudo mezclándolas, por lo que hace un poco de esto, luego un poco de eso, luego vuelve a la primera parte etc.
Cómo ver lo que hizo el optimizador
No puede ver el SQL en el que PostgreSQL optimiza su consulta, ya que convierte el SQL en una representación interna del árbol de consultas y luego lo modifica. Usted puede volcar el plan de consulta y compararlo con otras consultas.
No hay forma de "eliminar" ese plan de consulta o el árbol del plan interno de nuevo a SQL.
http://explain.depesz.com/ tiene un asistente de plan de consulta decente. Si es totalmente nuevo en los planes de consulta, etc. (en cuyo caso me sorprende que haya llegado tan lejos en esta publicación), PgAdmin tiene un visor gráfico de planes de consulta que proporciona mucha menos información pero es más simple.
Lectura relacionada:
Las capacidades de pushdown / pullup y aplanamiento continúan mejorando en cada versión . PostgreSQL generalmente tiene razón sobre las decisiones de subir / bajar / aplanar, pero no siempre, así que ocasionalmente tienes que (ab) usar un CTE o el OFFSET 0
pirateo. Si encuentra un caso así, informe un error del planificador de consultas.
Si está realmente interesado, también puede usar la debug_print_plans
opción para ver el plan de consulta sin procesar, pero le prometo que no quiere leer eso. De Verdad.
to_date
antes de la verificación en alguna versión posterior o debido a algún cambio en las estadísticas del optimizador. Para ejecutar una verificación de manera confiable antes de una función que solo debe ejecutarse después de la verificación, use unaCASE
instrucción.order by
la ejecución de la consulta era mucho más rápida que si no existieraorder by
. Esa es una de las razones por las que escribo mis consultas con combinaciones de tal manera que quisiera que se ejecutaran: es bueno tener un gran optimizador, pero creo que no es aconsejable confiar completamente en su destino para su resultado y escribir consultas sin pensar cómo semay be
ejecutó ... ¡Gran respuesta!SQL es un lenguaje declarativo: dices lo que quieres, no cómo hacerlo. El RDBMS elegirá la forma en que ejecutará la consulta, llamada plan de ejecución.
Érase una vez (hace 5-10 años), la forma en que se escribió una consulta tuvo un impacto directo en el plan de ejecución, pero hoy en día, la mayoría de los motores de bases de datos SQL utilizan un Optimizador basado en costos para la planificación. Es decir, evaluará diferentes estrategias para ejecutar la consulta, en función de sus estadísticas sobre los objetos de la base de datos, y elegirá la mejor.
La mayoría de las veces, es realmente el mejor, pero a veces el motor de base de datos tomará malas decisiones, lo que dará como resultado consultas muy lentas.
fuente
WITH(INDEX(<index>))
en MSSQL para forzar la elección del índice para una unión particular).date_day
realmente existe algún índice . Si no hay ninguno, entonces el optimizador no tiene muchos planes para comparar.