PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Warum ruft PostgreSQL meine STABLE/IMMUTABLE-Funktion mehrmals auf?

Die folgende Erweiterung Ihres Testcodes ist informativ:

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

AUSGABE:

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

Hier können wir sehen, dass während in der Auswahlliste die unveränderliche Funktion mehrmals aufgerufen wurde, in der where-Klausel einmal aufgerufen wurde, während die flüchtige Funktion dreimal aufgerufen wurde.

Wichtig ist nicht, dass PostgreSQL nur STABLE aufruft oder IMMUTABLE einmal mit denselben Daten funktionieren - Ihr Beispiel zeigt deutlich, dass dies nicht der Fall ist - es ist, dass es kann nur einmal anrufen. Oder vielleicht wird es zweimal aufgerufen, wenn es eine flüchtige Version 50 Mal aufrufen müsste, und so weiter.

Es gibt verschiedene Möglichkeiten, Stabilität und Unveränderlichkeit zu nutzen, mit unterschiedlichen Kosten und Nutzen. Um die Art der Einsparung bereitzustellen, die Sie mit Auswahllisten vorschlagen, müsste es die Ergebnisse zwischenspeichern und dann jedes Argument (oder jede Liste von Argumenten) in diesem Cache nachschlagen, bevor entweder das zwischengespeicherte Ergebnis zurückgegeben oder die Funktion für einen Cache aufgerufen wird -fehlschlagen. Dies wäre teurer als das Aufrufen Ihrer Funktion, selbst in dem Fall, in dem es einen hohen Prozentsatz an Cache-Treffern gab (es könnten 0% Cache-Treffer geben, was bedeutet, dass diese "Optimierung" zusätzliche Arbeit ohne absoluten Gewinn geleistet hat). Es könnte vielleicht nur den letzten Parameter und das letzte Ergebnis speichern, aber auch das könnte völlig nutzlos sein.

Dies gilt insbesondere, wenn man bedenkt, dass stabile und unveränderliche Funktionen oft die leichtesten Funktionen sind.

Mit der where-Klausel jedoch die Unveränderlichkeit von test_multi_calls1 ermöglicht PostgreSQL, die Abfrage tatsächlich aus der einfachen Bedeutung des angegebenen SQL-Codes neu zu strukturieren:

Zu einem völlig anderen Abfrageplan:

Dies ist die Art von Verwendung, die PostgreSQL von STABLE und IMMUTABLE macht – nicht das Zwischenspeichern von Ergebnissen, sondern das Umschreiben von Abfragen in andere Abfragen, die effizienter sind, aber die gleichen Ergebnisse liefern.

Beachten Sie auch, dass test_multi_calls1(30) vor test_multi_calls2(40) aufgerufen wird, egal in welcher Reihenfolge sie in der where-Klausel erscheinen. Das heißt, wenn der erste Aufruf dazu führt, dass keine Zeilen zurückgegeben werden (ersetzen Sie = 30 mit = 31 zum Testen) dann wird die flüchtige Funktion überhaupt nicht aufgerufen - wiederum unabhängig davon, was sich auf welcher Seite des and befindet .

Diese besondere Art des Umschreibens hängt von der Unveränderlichkeit oder Stabilität ab. Mit where test_multi_calls1(30) != num Das Umschreiben von Abfragen erfolgt für unveränderliche, aber nicht nur für stabile Funktionen. Mit where test_multi_calls1(num) != 30 es wird überhaupt nicht passieren (mehrere Aufrufe), obwohl andere Optimierungen möglich sind:

Ausdrücke, die nur STABLE- und IMMUTABLE-Funktionen enthalten, können mit Index-Scans verwendet werden. Ausdrücke, die VOLATILE-Funktionen enthalten, können dies nicht. Die Anzahl der Aufrufe kann sich verringern oder auch nicht, aber viel wichtiger ist, dass die Ergebnisse der Aufrufe dann im Rest der Abfrage viel effizienter verwendet werden (nur bei großen Tabellen wirklich wichtig, aber dann kann es eine massive Unterschied).

Denken Sie bei Volatilitätskategorien insgesamt nicht an Merkfähigkeit, sondern eher daran, dem Abfrageplaner von PostgreSQL die Möglichkeit zu geben, ganze Abfragen auf eine Weise umzustrukturieren, die logisch äquivalent (gleiche Ergebnisse), aber viel effizienter ist.