sql >> Base de Datos >  >> RDS >> PostgreSQL

¿Por qué PostgreSQL llama a mi función ESTABLE/IMMUTABLE varias veces?

La siguiente extensión de su código de prueba es informativa:

CREATE OR REPLACE FUNCTION test_multi_calls1(one integer)
RETURNS integer
AS $BODY$
BEGIN
    RAISE NOTICE 'Immutable called with %', one;
    RETURN one;
END;
$BODY$ LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION test_multi_calls2(one integer)
RETURNS integer
AS $BODY$
BEGIN
    RAISE NOTICE 'Volatile called with %', one;
    RETURN one;
END;
$BODY$ LANGUAGE plpgsql VOLATILE;

WITH data AS
(
    SELECT 10 AS num
    UNION ALL SELECT 10
    UNION ALL SELECT 20
)
SELECT test_multi_calls1(num)
FROM data
where test_multi_calls2(40) = 40
and test_multi_calls1(30) = 30

SALIDA:

NOTICE:  Immutable called with 30
NOTICE:  Volatile called with 40
NOTICE:  Immutable called with 10
NOTICE:  Volatile called with 40
NOTICE:  Immutable called with 10
NOTICE:  Volatile called with 40
NOTICE:  Immutable called with 20

Aquí podemos ver que mientras que en la lista de selección la función inmutable se llamó varias veces, en la cláusula where se llamó una vez, mientras que la función volátil se llamó tres veces.

Lo importante no es que PostgreSQL solo llamará a un STABLE o IMMUTABLE funcionar una vez con los mismos datos; su ejemplo muestra claramente que este no es el caso; es que puede llámalo una sola vez. O tal vez lo llamará dos veces cuando tendría que llamar a una versión volátil 50 veces, y así sucesivamente.

Hay diferentes formas de aprovechar la estabilidad y la inmutabilidad, con diferentes costos y beneficios. Para proporcionar el tipo de ahorro que sugiere que debería hacer con listas de selección, tendría que almacenar en caché los resultados y luego buscar cada argumento (o lista de argumentos) en este caché antes de devolver el resultado almacenado en caché o llamar a la función en un caché -señorita. Esto sería más costoso que llamar a su función, incluso en el caso de que hubiera un alto porcentaje de aciertos de caché (podría haber 0% de aciertos de caché, lo que significa que esta "optimización" hizo un trabajo adicional sin ninguna ganancia). Podría almacenar tal vez solo el último parámetro y el resultado, pero nuevamente, eso podría ser completamente inútil.

Esto es especialmente así si se tiene en cuenta que las funciones estables e inmutables suelen ser las funciones más ligeras.

Sin embargo, con la cláusula where, la inmutabilidad de test_multi_calls1 permite que PostgreSQL realmente reestructure la consulta desde el significado simple del SQL dado:

A un plan de consulta completamente diferente:

Este es el tipo de uso que PostgreSQL hace de ESTABLE e INMUTABLE:no el almacenamiento en caché de los resultados, sino la reescritura de consultas en diferentes consultas que son más eficientes pero dan los mismos resultados.

Tenga en cuenta también que test_multi_calls1(30) se llama antes que test_multi_calls2(40) sin importar el orden en que aparecen en la cláusula where. Esto significa que si la primera llamada da como resultado que no se devuelvan filas (reemplace = 30 con = 31 para probar) entonces la función volátil no se llamará en absoluto, nuevamente independientemente de cuál esté en qué lado del and .

Este tipo particular de reescritura depende de la inmutabilidad o estabilidad. Con where test_multi_calls1(30) != num la reescritura de consultas ocurrirá para funciones inmutables pero no meramente estables. Con where test_multi_calls1(num) != 30 no sucederá en absoluto (múltiples llamadas) aunque hay otras optimizaciones posibles:

Las expresiones que contienen solo funciones ESTABLES e INMUTABLES se pueden usar con exploraciones de índice. Las expresiones que contienen funciones VOLÁTILES no pueden. El número de llamadas puede disminuir o no, pero lo que es mucho más importante, los resultados de las llamadas se utilizarán de una manera mucho más eficiente en el resto de la consulta (realmente solo importa en tablas grandes, pero luego puede hacer un cambio masivo). diferencia).

En total, no piense en las categorías de volatilidad en términos de memorización, sino más bien en términos de dar al planificador de consultas de PostgreSQL oportunidades para reestructurar consultas completas de manera que sean lógicamente equivalentes (los mismos resultados) pero mucho más eficientes.