Plan de consulta de Postgres de una invocación de función escrita en plpgsql


19

Es posible que al utilizar el pgadmino plsqlpara obtener una bodega de un plan de consulta para una instrucción SQL ejecutadas dentro de una u Ser d efined f unción (UDF) con EXPLAIN. Entonces, ¿cómo obtengo el plan de consulta para una invocación particular de un UDF? Veo el UDF abstraído en una sola operación F()en pgadmin.

He mirado la documentación pero no pude encontrar nada.

Actualmente estoy sacando las declaraciones y ejecutándolas manualmente. Pero esto no va a ser suficiente para consultas grandes.

Por ejemplo, considere el UDF a continuación. Este UDF, aunque tiene la capacidad de imprimir su cadena de consulta, no funcionará con una copia-pegar ya que tiene una tabla temporal local creada, que no existe cuando la pega y ejecuta.

CREATE OR REPLACE FUNCTION get_paginated_search_results(
    forum_id_ INTEGER,
    query_    CHARACTER VARYING,
    from_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    to_date_ TIMESTAMP WITHOUT TIME ZONE DEFAULT NULL,
    in_categories_ INTEGER[] DEFAULT '{}')
RETURNS SETOF post_result_entry AS $$
DECLARE
    join_string CHARACTER VARYING := ' ';
    from_where_date CHARACTER VARYING := ' ';
    to_where_date CHARACTER VARYING := ' ';
    query_string_ CHARACTER VARYING := ' ';
BEGIN
    IF NOT from_date_ IS NULL THEN
        from_where_date := ' AND fp.posted_at > ''' || from_date_ || '''';
    END IF;

    IF NOT to_date_ IS NULL THEN
        to_where_date := ' AND fp.posted_at < ''' || to_date_ || '''';
    END IF;

    CREATE LOCAL TEMP TABLE un_cat(id) ON COMMIT DROP AS (select * from unnest(in_categories_)) ;

    if in_categories_ != '{}' THEN
        join_string := ' INNER JOIN forum_topics ft ON fp.topic_id = ft.id ' ||
        ' INNER JOIN un_cat uc ON uc.id = ft.category_id ' ;
    END IF;

    query_string_ := '
    SELECT index,posted_at,post_text,name,join_date,quotes
    FROM forum_posts fp
    INNER JOIN forum_user fu ON
    fu.forum_id = fp.forum_id AND fu.id = fp.user_id' ||
        join_string
    ||
    'WHERE fu.forum_id = ' || forum_id_ || ' AND
    to_tsvector(''english'',fp.post_text) @@ to_tsquery(''english'','''|| query_||''')' || 
        from_where_date || 
        to_where_date
    ||';';

    RAISE NOTICE '%', query_string_ ;

    RETURN QUERY
    EXECUTE query_string_;
END;
$$ LANGUAGE plpgsql;

Respuestas:


16

Debería poder utilizar la explicación automática . Enciéndelo y

SET auto_explain.log_min_duration = 0;

y debe obtener los planes en su registro para todas las declaraciones ejecutadas en esa sesión.

Es posible que también desee establecer

SET auto_explain.log_analyze = true; pero esencialmente ejecutará todo el doble: una vez para 'real' y otra para EXPLICAR ANALIZAR. Durante una fase de prueba de rendimiento sin temporización, este resultado puede ser mucho más útil que los planes EXPLAIN solo, ya que proporciona el plan que realmente sucedió.


44
Como @Erwin señala a continuación, también debe establecer auto_explain.log_nested_statements = ON.
rfusca

Gracias, eso hizo el truco. Es una pena que esta funcionalidad no sea accesible a través de una GUI.
Hassan Syed el

@rfusca esencialmente ejecutarás todo el doble, ¿ dónde está la prueba de eso? Algunos experimentos que no mostré este comportamiento.
Sebastian Dressler

Tenga en cuenta que esto hace referencia a una base de datos de 7 años en este momento. Es probable que ya no funcione así si no estás viendo los mismos resultados.
rfusca

16

Además del consejo de @ rfusca: las instrucciones SQL dentro de las funciones plpgsql se consideran instrucciones anidadas y debe establecer el parámetro adicional auto_explain.log_nested_statements.

A diferencia de otras extensiones, no tiene que ejecutar CREATE EXTENSIONesta. Simplemente cárguelo dinámicamente en su sesión con LOAD. Su sesión podría verse así:

LOAD 'auto_explain';
SET auto_explain.log_min_duration = 1; -- exclude very fast trivial queries
SET auto_explain.log_nested_statements = ON; -- statements inside functions
-- SET auto_explain.log_analyze = ON; -- get actual times, too
SELECT * FROM get_paginated_search_results(...);

Puede producir mucha salida de registro.
El manual actual en auto_explain.
Depesz escribió un artículo de blog al respecto cuando se presentó con PostgreSQL 8.4.


+1: ha pasado tanto tiempo que olvidé la necesidad de establecer la línea
log_nested_statements

3
Te mereces el crédito por sacar la herramienta adecuada de todos modos.
Erwin Brandstetter

Tengo una base de datos postgres en el servicio administrado de Amazon (RDS), para lo cual LOAD 'auto_explain';regresa ERROR: access to library "auto_explain" is not allowed. Que en ese caso? He tenido éxito al piratear mis funciones, return query explain select …pero eso es laborioso y lento.
poshest
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.