PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Pourquoi PostgreSQL appelle-t-il ma fonction STABLE/IMMUTABLE plusieurs fois ?

L'extension suivante de votre code de test est informative :

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

SORTIE :

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

Ici, nous pouvons voir que, alors que dans la liste de sélection, la fonction immuable a été appelée plusieurs fois, dans la clause where, elle a été appelée une fois, tandis que la volatile a été appelée trois fois.

L'important n'est pas que PostgreSQL n'appellera qu'un STABLE ou IMMUTABLE fonctionner une fois avec les mêmes données - votre exemple montre clairement que ce n'est pas le cas - c'est que cela peut appelez-le une seule fois. Ou peut-être qu'il l'appellera deux fois alors qu'il devrait appeler une version volatile 50 fois, et ainsi de suite.

Il existe différentes manières de tirer parti de la stabilité et de l'immuabilité, avec des coûts et des avantages différents. Pour fournir le type d'économie que vous suggérez qu'il devrait faire avec les listes de sélection, il devrait mettre en cache les résultats, puis rechercher chaque argument (ou liste d'arguments) dans ce cache avant de renvoyer le résultat mis en cache ou d'appeler la fonction sur un cache -Mademoiselle. Cela reviendrait plus cher que d'appeler votre fonction, même dans le cas où il y aurait un pourcentage élevé d'accès au cache (il pourrait y avoir 0 % d'accès au cache, ce qui signifie que cette "optimisation" a fait un travail supplémentaire sans aucun gain). Il pourrait ne stocker que le dernier paramètre et le résultat, mais encore une fois, cela pourrait être complètement inutile.

Cela est d'autant plus vrai que les fonctions stables et immuables sont souvent les fonctions les plus légères.

Avec la clause where cependant, l'immuabilité de test_multi_calls1 permet à PostgreSQL de restructurer réellement la requête à partir du sens ordinaire du SQL donné :

Vers un plan de requête entièrement différent :

C'est le genre d'utilisation que PostgreSQL fait de STABLE et IMMUTABLE - pas la mise en cache des résultats, mais la réécriture des requêtes en différentes requêtes qui sont plus efficaces mais donnent les mêmes résultats.

Notez également que test_multi_calls1(30) est appelé avant test_multi_calls2(40) quel que soit l'ordre dans lequel ils apparaissent dans la clause where. Cela signifie que si le premier appel ne renvoie aucune ligne (remplacez = 30 avec = 31 à tester) alors la fonction volatile ne sera pas appelée du tout - encore une fois, quel que soit le côté du and .

Ce type particulier de réécriture dépend de l'immuabilité ou de la stabilité. Avec where test_multi_calls1(30) != num la réécriture de la requête se produira pour les fonctions immuables mais pas pour les fonctions simplement stables. Avec where test_multi_calls1(num) != 30 cela n'arrivera pas du tout (appels multiples) bien qu'il y ait d'autres optimisations possibles :

Les expressions contenant uniquement des fonctions STABLE et IMMUTABLE peuvent être utilisées avec des parcours d'index. Les expressions contenant des fonctions VOLATILE ne le peuvent pas. Le nombre d'appels peut ou non diminuer, mais plus important encore, les résultats des appels seront alors utilisés de manière beaucoup plus efficace dans le reste de la requête (cela n'a vraiment d'importance que sur les grandes tables, mais cela peut alors faire un énorme différence).

Au total, ne pensez pas aux catégories de volatilité en termes de mémorisation, mais plutôt en termes de donner au planificateur de requêtes de PostgreSQL des opportunités de restructurer des requêtes entières de manière logiquement équivalente (mêmes résultats) mais beaucoup plus efficace.