¿Se aplican las cláusulas WHERE en el orden en que están escritas?

36

Estoy tratando de optimizar una consulta que se ve en una tabla grande (37 millones de filas) y tengo una pregunta sobre el orden en que se ejecutan las operaciones en una 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')
    )

¿Se WHEREejecutan las cláusulas para el rango de fechas antes de la subconsulta? ¿Es una buena manera de poner las cláusulas más restrictivas primero para evitar grandes bucles para otras cláusulas, a fin de hacer una ejecución más rápida?

Ahora las consultas tardan tanto tiempo en ejecutarse.

Jorge Vega Sánchez
fuente

Respuestas:

68

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 WHEREclá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 EXISTSy NOT EXISTSconsultas.

  • 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_costdecirle 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 LIMITo OFFSETno 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 WITHconsulta) 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_barriervistas 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 0pirateo. Si encuentra un caso así, informe un error del planificador de consultas.


Si está realmente interesado, también puede usar la debug_print_plansopción para ver el plan de consulta sin procesar, pero le prometo que no quiere leer eso. De Verdad.

Craig Ringer
fuente
Wow ... respuesta bastante completa :-) Uno de los casos en los que tenía planes lentos con Postgresql (así como con otros motores DB conocidos, como Oracle), es con correlaciones entre columnas o múltiples combinaciones correlacionadas. A menudo terminará haciendo bucles anidados, pensando que solo hay unas pocas filas en este punto del plan, cuando en realidad hay muchos miles. Una forma de optimizar este tipo de consultas es 'establecer enable_nestloop = off;' por la duración de la consulta.
alci
Me encontré con una situación en la que v9.5.5 estaba tratando de aplicar TO_DATE antes de verificar si podía aplicarse, en una simple consulta de cláusula 7 where. El orden importaba.
user1133275
@ user1133275 En ese caso, solo funcionó para usted por casualidad, porque las estimaciones de costos de cálculo fueron las mismas. PostgreSQL aún podría decidir ejecutarse to_dateantes 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 una CASEinstrucción.
Craig Ringer
una de las mayores respuestas que he nunca visto en SO! ¡Pulgares arriba, hombre!
62mkv
Experimenté situaciones en las que, al agregar consultas simples, order byla ejecución de la consulta era mucho más rápida que si no existiera order 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 se may beejecutó ... ¡Gran respuesta!
Greg0ry
17

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.

alci
fuente
Cabe señalar que en algunos RDBMS el orden de consulta sigue siendo significativo, pero para los más avanzados todo lo que dice es cierto en la práctica, así como en la teoría. Cuando el planificador de consultas elige malas elecciones de orden de ejecución, generalmente hay sugerencias de consultas disponibles para impulsarlo en una dirección más eficiente (como WITH(INDEX(<index>))en MSSQL para forzar la elección del índice para una unión particular).
David Spillett
La pregunta es si date_dayrealmente existe algún índice . Si no hay ninguno, entonces el optimizador no tiene muchos planes para comparar.
jkavalik