PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Por que o PostgreSQL está chamando minha função STABLE/IMMUTABLE várias vezes?


A seguinte extensão do seu código de teste é 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

RESULTADO:
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

Aqui podemos ver que enquanto na lista de seleção a função imutável foi chamada várias vezes, na cláusula where ela foi chamada uma vez, enquanto a volátil foi chamada três vezes.

O importante não é que o PostgreSQL só chamará um STABLE ou IMMUTABLE funcionar uma vez com os mesmos dados - seu exemplo mostra claramente que esse não é o caso - é que pode chame-o apenas uma vez. Ou talvez ele a chame duas vezes quando teria que chamar uma versão volátil 50 vezes e assim por diante.

Existem diferentes maneiras de aproveitar a estabilidade e a imutabilidade, com diferentes custos e benefícios. Para fornecer o tipo de economia que você está sugerindo que ele deveria fazer com listas de seleção, ele teria que armazenar em cache os resultados e, em seguida, pesquisar cada argumento (ou lista de argumentos) neste cache antes de retornar o resultado em cache ou chamar a função em um cache -senhorita. Isso seria mais caro do que chamar sua função, mesmo no caso em que houvesse uma alta porcentagem de acertos de cache (pode haver 0% de acertos de cache, o que significa que essa "otimização" fez um trabalho extra sem absolutamente nenhum ganho). Ele pode armazenar talvez apenas o último parâmetro e resultado, mas novamente isso pode ser completamente inútil.

Isto é especialmente verdade considerando que funções estáveis ​​e imutáveis ​​são frequentemente as funções mais leves.

No entanto, com a cláusula where, a imutabilidade de test_multi_calls1 permite que o PostgreSQL realmente reestruture a consulta a partir do significado simples do SQL fornecido:

Para um plano de consulta totalmente diferente:

Este é o tipo de uso que o PostgreSQL faz de STABLE e IMMUTABLE - não o cache de resultados, mas a reescrita de consultas em consultas diferentes que são mais eficientes, mas fornecem os mesmos resultados.

Observe também que test_multi_calls1(30) é chamado antes de test_multi_calls2(40) não importa a ordem em que aparecem na cláusula where. Isso significa que se a primeira chamada resultar em nenhuma linha sendo retornada (substitua = 30 com = 31 para testar) então a função volátil não será chamada - novamente, independentemente de qual lado do and .

Esse tipo particular de reescrita depende de imutabilidade ou estabilidade. Com where test_multi_calls1(30) != num a reescrita de consulta acontecerá para funções imutáveis, mas não para funções meramente estáveis. Com where test_multi_calls1(num) != 30 isso não acontecerá (várias chamadas), embora existam outras otimizações possíveis:

Expressões contendo apenas funções STABLE e IMMUTABLE podem ser usadas com varreduras de índice. Expressões contendo funções VOLATILE não podem. O número de chamadas pode ou não diminuir, mas muito mais importante os resultados das chamadas serão usados ​​de uma maneira muito mais eficiente no resto da consulta (só importa em tabelas grandes, mas pode fazer um enorme diferença).

Em suma, não pense nas categorias de volatilidade em termos de memorização, mas sim em termos de dar ao planejador de consultas do PostgreSQL oportunidades para reestruturar consultas inteiras de maneiras que sejam logicamente equivalentes (mesmos resultados), mas muito mais eficientes.