EXPLAIN

EXPLAIN — mostrar el plan de ejecución de una sentencia

Synopsis

EXPLAIN [ ( opción [, ...] ) ] sentencia

donde opción puede ser uno de:

    ANALYZE [ booleano ]
    VERBOSE [ booleano ]
    COSTS [ booleano ]
    SETTINGS [ booleano ]
    GENERIC_PLAN [ booleano ]
    BUFFERS [ booleano ]
    SERIALIZE [ { NONE | TEXT | BINARY } ]
    WAL [ booleano ]
    TIMING [ booleano ]
    SUMMARY [ booleano ]
    MEMORY [ booleano ]
    FORMAT { TEXT | XML | JSON | YAML }

Descripción

Este comando muestra el plan de ejecución que el planificador de PostgreSQL genera para la sentencia suministrada. El plan de ejecución muestra cómo se escanearán la(s) tabla(s) a la(s) que hace referencia la sentencia — mediante escaneo secuencial simple, escaneo de índice, etc. — y, si se hace referencia a varias tablas, qué algoritmos de unión (join) se utilizarán para reunir las filas requeridas de cada tabla de entrada.

La parte más crítica de la pantalla es el costo estimado de ejecución de la sentencia, que es la estimación del planificador sobre cuánto tiempo tardará en ejecutarse la sentencia (medido en unidades de costo que son arbitrarias, pero que convencionalmente significan lecturas de páginas de disco). En realidad, se muestran dos números: el costo de inicio (start-up cost) antes de que se pueda devolver la primera fila, y el costo total para devolver todas las filas. Para la mayoría de las consultas, lo que importa es el costo total, pero en contextos como una subconsulta en EXISTS, el planificador elegirá el costo de inicio más pequeño en lugar del costo total más pequeño (ya que el ejecutor se detendrá después de obtener una fila, de todos modos). Además, si limitas el número de filas a devolver con una cláusula LIMIT, el planificador realiza una interpolación adecuada entre los costos de los extremos para estimar qué plan es realmente el más barato.

La opción ANALYZE hace que la sentencia se ejecute realmente, no solo se planifique. Luego, se añaden estadísticas de tiempo de ejecución reales a la pantalla, incluyendo el tiempo total transcurrido en cada nodo del plan (en milisegundos) y el número total de filas que realmente devolvió. Esto es útil para ver si las estimaciones del planificador son cercanas a la realidad.

Important

Ten en cuenta que la sentencia se ejecuta realmente cuando se utiliza la opción ANALYZE. Aunque EXPLAIN descartará cualquier salida que devolvería un SELECT, otros efectos secundarios de la sentencia ocurrirán de manera habitual. Si deseas usar EXPLAIN ANALYZE en una sentencia INSERT, UPDATE, DELETE, MERGE, CREATE TABLE AS o EXECUTE sin permitir que el comando afecte tus datos, utiliza este enfoque:

BEGIN;
EXPLAIN ANALYZE ...;
ROLLBACK;

Parámetros

ANALYZE

Lleva a cabo el comando y muestra los tiempos de ejecución reales y otras estadísticas. Este parámetro tiene como valor por omisión FALSE.

VERBOSE

Muestra información adicional sobre el plan. Específicamente, incluye la lista de columnas de salida para cada nodo en el árbol del plan, califica los nombres de tablas y funciones con su esquema, etiqueta siempre las variables en las expresiones con su alias de la tabla de rango, y muestra siempre el nombre de cada disparador para el cual se muestran estadísticas. También se mostrará el identificador de consulta si se ha calculado uno, consulta la compute_query_id para obtener más detalles. Este parámetro tiene como valor por omisión FALSE.

COSTS

Incluye información sobre el costo de inicio estimado y el costo total de cada nodo del plan, así como el número estimado de filas y el ancho estimado de cada fila. Este parámetro tiene como valor por omisión TRUE.

SETTINGS

Incluye información sobre los parámetros de configuración. Específicamente, incluye opciones que afectan la planificación de consultas con un valor diferente al valor por omisión incorporado. Este parámetro tiene como valor por omisión FALSE.

GENERIC_PLAN

Permite que la sentencia contenga marcadores de posición de parámetros como $1, y genera un plan genérico que no dependa de los valores de esos parámetros. Consulta la PREPARE para obtener detalles sobre los planes genéricos y los tipos de sentencias que admiten parámetros. Este parámetro no se puede usar junto con ANALYZE. Tiene como valor por omisión FALSE.

BUFFERS

Incluye información sobre el uso de búferes. Específicamente, incluye el número de bloques compartidos (shared blocks) acertados (hit), leídos, modificados (dirtied) y escritos, el número de bloques locales acertados, leídos, modificados y escritos, el número de bloques temporales leídos y escritos, y el tiempo dedicado a leer y escribir bloques de archivos de datos, bloques locales y bloques de archivos temporales (en milisegundos) si track_io_timing está activado. Un acierto (hit) significa que se evitó una lectura porque el bloque ya se encontraba en caché cuando se necesitó. Los bloques compartidos contienen datos de tablas e índices normales; los bloques locales contienen datos de tablas e índices temporales; mientras que los bloques temporales contienen datos de trabajo a corto plazo utilizados en ordenamientos, hashes, nodos de plan Materialize y casos similares. El número de bloques modificados (dirtied) indica el número de bloques previamente no modificados que fueron cambiados por esta consulta; mientras que el número de bloques escritos indica el número de bloques previamente modificados que fueron desalojados de la caché por este backend durante el procesamiento de la consulta. El número de bloques mostrados para un nodo de nivel superior incluye los utilizados por todos sus nodos hijos. En formato de texto, solo se imprimen los valores distintos de cero. La información de los búferes se incluye automáticamente cuando se utiliza ANALYZE.

SERIALIZE

Incluye información sobre el costo de serializar los datos de salida de la consulta, es decir, convertirlos a formato de texto o binario para enviarlos al cliente. Esto puede ser una parte significativa del tiempo requerido para la ejecución normal de la consulta si las funciones de salida del tipo de datos son costosas o si los valores TOAST deben recuperarse del almacenamiento fuera de línea (out-of-line storage). El comportamiento por omisión de EXPLAIN, SERIALIZE NONE, no realiza estas conversiones. Si se especifica SERIALIZE TEXT o SERIALIZE BINARY, se realizan las conversiones correspondientes y se mide el tiempo dedicado a ello (a menos que se especifique TIMING OFF). Si también se especifica la opción BUFFERS, se contarán también los accesos a los búferes implicados en las conversiones. En ningún caso, sin embargo, EXPLAIN enviará realmente los datos resultantes al cliente; por lo tanto, los costos de transmisión de red no se pueden investigar de esta manera. La serialización solo se puede activar cuando ANALYZE también está activado. Si se escribe SERIALIZE sin argumento, se asume TEXT.

WAL

Incluye información sobre la generación de registros de WAL. Específicamente, incluye el número de registros, el número de imágenes de página completa (fpi), la cantidad de WAL generada en bytes y el número de veces que los búferes de WAL se llenaron. En formato de texto, solo se imprimen los valores distintos de cero. Este parámetro solo se puede utilizar cuando ANALYZE también está activado. Tiene como valor por omisión FALSE.

TIMING

Incluye en la salida el tiempo de inicio real y el tiempo dedicado a cada nodo. La sobrecarga de leer repetidamente el reloj del sistema puede ralentizar la consulta significativamente en algunos sistemas, por lo que puede ser útil establecer este parámetro en FALSE cuando solo se necesitan los conteos de filas reales y no los tiempos exactos. El tiempo de ejecución de toda la sentencia se mide siempre, incluso cuando el cronometraje a nivel de nodo se desactiva con esta opción. Este parámetro solo se puede utilizar cuando ANALYZE también está activado. Tiene como valor por omisión TRUE.

SUMMARY

Incluye información de resumen (por ejemplo, información de tiempos totales) después del plan de consulta. La información de resumen se incluye por omisión cuando se utiliza ANALYZE, pero de lo contrario no se incluye por omisión, aunque se puede activar mediante esta opción. El tiempo de planificación en EXPLAIN EXECUTE incluye el tiempo requerido para recuperar el plan de la caché y el tiempo requerido para la replanificación, si es necesario.

MEMORY

Incluye información sobre el consumo de memoria por parte de la fase de planificación de la consulta. Específicamente, incluye la cantidad precisa de almacenamiento utilizado por las estructuras en memoria del planificador, así como la memoria total considerando la sobrecarga de asignación. Este parámetro tiene como valor por omisión FALSE.

FORMAT

Especifica el formato de salida, que puede ser TEXT, XML, JSON o YAML. La salida que no es de texto contiene la misma información que el formato de salida de texto, pero es más fácil de analizar para los programas. Este parámetro tiene como valor por omisión TEXT.

booleano

Especifica si la opción seleccionada debe activarse o desactivarse. Puedes escribir TRUE, ON o 1 para activar la opción, y FALSE, OFF o 0 para desactivarla. El valor booleano también se puede omitir, en cuyo caso se asume TRUE.

sentencia

Cualquier sentencia SELECT, INSERT, UPDATE, DELETE, MERGE, VALUES, EXECUTE, DECLARE, CREATE TABLE AS o CREATE MATERIALIZED VIEW AS cuyo plan de ejecución desees ver.

Salidas

El resultado del comando es una descripción textual del plan seleccionado para la sentencia, opcionalmente anotada con estadísticas de ejecución. La Section 14.1 describe la información proporcionada.

Notas

Para permitir que el planificador de consultas de PostgreSQL tome decisiones razonablemente informadas al optimizar las consultas, los datos de pg_statistic deben estar actualizados para todas las tablas utilizadas en la consulta. Normalmente, el demonio de autovacuum se encargará de ello automáticamente. Pero si una tabla ha tenido cambios sustanciales recientemente en su contenido, es posible que debas realizar un ANALYZE manual en lugar de esperar a que autovacuum se ponga al día con los cambios.

Para medir el costo en tiempo de ejecución de cada nodo en el plan de ejecución, la implementación actual de EXPLAIN ANALYZE añade una sobrecarga de análisis de rendimiento (profiling) a la ejecución de la consulta. Como resultado, ejecutar EXPLAIN ANALYZE en una consulta a veces puede tomar significativamente más tiempo que ejecutar la consulta normalmente. La cantidad de sobrecarga depende de la naturaleza de la consulta, así como de la plataforma utilizada. El peor caso ocurre para los nodos del plan que en sí mismos requieren muy poco tiempo por ejecución, y en máquinas que tienen llamadas del sistema operativo relativamente lentas para obtener la hora del día.

Ejemplos

Para mostrar el plan de una consulta simple en una tabla con una sola columna de tipo integer y 10000 filas:

EXPLAIN SELECT * FROM foo;

                       QUERY PLAN
 ---------------------------------------------------------
  Seq Scan on foo  (cost=0.00..155.00 rows=10000 width=4)
(1 row)

Aquí está la misma consulta, con formato de salida JSON:

EXPLAIN (FORMAT JSON) SELECT * FROM foo;
           QUERY PLAN
--------------------------------
 [                             +
   {                           +
     "Plan": {                 +
       "Node Type": "Seq Scan",+
       "Relation Name": "foo", +
       "Alias": "foo",         +
       "Startup Cost": 0.00,   +
       "Total Cost": 155.00,   +
       "Plan Rows": 10000,     +
       "Plan Width": 4         +
     }                         +
   }                           +
 ]
(1 row)

Si hay un índice y usamos una consulta con una condición WHERE indexable, EXPLAIN podría mostrar un plan diferente:

EXPLAIN SELECT * FROM foo WHERE i = 4;

                          QUERY PLAN
--------------------------------------------------------------
  Index Scan using fi on foo  (cost=0.00..5.98 rows=1 width=4)
    Index Cond: (i = 4)
(2 rows)

Aquí está la misma consulta, pero en formato YAML:

EXPLAIN (FORMAT YAML) SELECT * FROM foo WHERE i='4';
          QUERY PLAN
-------------------------------
 - Plan:                      +
     Node Type: "Index Scan"  +
     Scan Direction: "Forward"+
     Index Name: "fi"         +
     Relation Name: "foo"     +
     Alias: "foo"             +
     Startup Cost: 0.00       +
     Total Cost: 5.98         +
     Plan Rows: 1             +
     Plan Width: 4            +
     Index Cond: "(i = 4)"
(1 row)

El formato XML se deja como ejercicio para el lector.

Aquí está el mismo plan con las estimaciones de costo suprimidas:

EXPLAIN (COSTS FALSE) SELECT * FROM foo WHERE i = 4;

        QUERY PLAN
----------------------------
 Index Scan using fi on foo
   Index Cond: (i = 4)
(2 rows)

Aquí está un ejemplo de un plan de consulta para una consulta que utiliza una función de agregación:

EXPLAIN SELECT sum(i) FROM foo WHERE i < 10;

                              QUERY PLAN
-------------------------------------------------------------------​--
 Aggregate  (cost=23.93..23.93 rows=1 width=4)
   ->  Index Scan using fi on foo  (cost=0.00..23.92 rows=6 width=4)
         Index Cond: (i < 10)
(3 rows)

Aquí está un ejemplo del uso de EXPLAIN EXECUTE para mostrar el plan de ejecución de una consulta preparada:

PREPARE query(int, int) AS SELECT sum(bar) FROM test
    WHERE id > $1 AND id < $2
    GROUP BY foo;

EXPLAIN ANALYZE EXECUTE query(100, 200);

                                                       QUERY PLAN
-------------------------------------------------------------------​------------------------------------------------------
 HashAggregate  (cost=10.77..10.87 rows=10 width=12) (actual time=0.043..0.044 rows=10.00 loops=1)
   Group Key: foo
   Batches: 1  Memory Usage: 24kB
   Buffers: shared hit=4
   ->  Index Scan using test_pkey on test  (cost=0.29..10.27 rows=99 width=8) (actual time=0.009..0.025 rows=99.00 loops=1)
         Index Cond: ((id > 100) AND (id < 200))
         Index Searches: 1
         Buffers: shared hit=4
 Planning Time: 0.244 ms
 Execution Time: 0.073 ms
(10 rows)

Por supuesto, los números específicos que se muestran aquí dependen del contenido real de las tablas involucradas. También ten en cuenta que los números, e incluso la estrategia de consulta seleccionada, pueden variar entre las versiones de PostgreSQL debido a las mejoras del planificador. Además, el comando ANALYZE utiliza un muestreo aleatorio para estimar las estadísticas de los datos; por lo tanto, es posible que las estimaciones de costo cambien después de una nueva ejecución de ANALYZE, incluso si la distribución real de los datos en la tabla no ha cambiado.

Ten en cuenta que el ejemplo anterior mostró un plan personalizado (custom plan) para los valores de parámetros específicos dados en EXECUTE. También podríamos desear ver el plan genérico para una consulta parametrizada, lo cual se puede hacer con GENERIC_PLAN:

EXPLAIN (GENERIC_PLAN)
  SELECT sum(bar) FROM test
    WHERE id > $1 AND id < $2
    GROUP BY foo;

                                  QUERY PLAN
-------------------------------------------------------------------​------------
 HashAggregate  (cost=26.79..26.89 rows=10 width=12)
   Group Key: foo
   ->  Index Scan using test_pkey on test  (cost=0.29..24.29 rows=500 width=8)
         Index Cond: ((id > $1) AND (id < $2))
(4 rows)

En este caso, el analizador (parser) infirió correctamente que $1 y $2 debían tener el mismo tipo de datos que id, por lo que la falta de información sobre el tipo de parámetro de PREPARE no fue un problema. En otros casos, puede ser necesario especificar explícitamente los tipos para los símbolos de los parámetros, lo cual se puede hacer mediante una conversión de tipo (casting), por ejemplo:

EXPLAIN (GENERIC_PLAN)
  SELECT sum(bar) FROM test
    WHERE id > $1::integer AND id < $2::integer
    GROUP BY foo;

Compatibilidad

No hay ninguna sentencia EXPLAIN definida en el estándar SQL.

La siguiente sintaxis se utilizaba antes de la versión 9.0 de PostgreSQL y todavía se admite:

EXPLAIN [ ANALYZE ] [ VERBOSE ] sentencia

Ten en cuenta que en esta sintaxis, las opciones deben especificarse exactamente en el orden mostrado.

Véase también

ANALYZE