Führt Postgres die Berechnung berechneter Spalten durch, die in einer Ansicht nicht ausgewählt sind?

8

Ich versuche, die Auswirkungen der Auswahl von Daten aus einer Ansicht auf die Leistung zu verstehen, wobei eine der Spalten in einer Ansicht von anderen Daten in der Originaltabelle abhängt.

Wird die Berechnung unabhängig davon durchgeführt, ob sich die berechnete Spalte in der Liste der ausgewählten Spalten befindet oder nicht?

Wenn ich einen Tisch hätte und die Ansicht so deklariert wäre

CREATE TABLE price_data (
    ticker     text,          -- Ticker of the stock
    ddate      date,          -- Date for this price
    price      float8,        -- Closing price on this date
    factor     float8         -- Factor to convert this price to USD
);

CREATE VIEW prices AS 
    SELECT ticker, 
           ddate,
           price,
           factor,
           price * factor as price_usd
    FROM price_data

Würde diese Multiplikation in einer Abfrage wie der folgenden durchgeführt?

select ticker, ddate, price, factor from prices

Gibt es eine Referenz, die dies auf die eine oder andere Weise garantiert? Ich habe die Dokumentation zum Regelsystem in Postgres gelesen, aber ich denke, die Antwort liegt wirklich beim Optimierer, da nichts in der Dokumentation zum Regelsystem darauf hinweist, dass es nicht ausgewählt werden würde.

Ich vermute im obigen Fall, dass die Berechnung nicht durchgeführt wird. Ich habe die Ansicht geändert, um Division anstelle von Multiplikation zu verwenden, und ein 0for factorin eingefügt price_data. Die obige Abfrage ist nicht fehlgeschlagen. Wenn die Abfrage jedoch geändert wurde, um die berechnete Spalte auszuwählen, ist die geänderte Abfrage fehlgeschlagen.

Gibt es eine Möglichkeit zu verstehen, welche Berechnungen durchgeführt werden, wenn a selectausgeführt wird? Ich schätze, ich suche nach etwas Ähnlichem EXPLAIN, das mir aber auch Auskunft über die durchgeführten Berechnungen gibt.

Varun Madiath
quelle
1
Dies ist eine ausgezeichnete Frage der Art, die wir an diesem SE
Gaius

Antworten:

6

Wie @Laurenz sagte, ist Ihre Analyse korrekt: Der Optimierer vermeidet die Auswertung von Spaltenausdrücken, die das Ergebnis der Abfrage nicht beeinflussen (und Ihr Versuch, einen Fehler beim Teilen durch Null zu erzwingen, ist ein Beweis dafür).

Dies hängt davon ab, welche Spalten Sie auswählen, aber auch von der Volatilitätskategorie der Spaltenausdrücke. Dem Optimierer steht es frei, Funktionsaufrufe wegzulassen immutableund zu stableverwenden, wenn ihre Ausgabe nie verwendet wird, da sie das Ergebnis nicht beeinflussen können. volatileFunktionen können jedoch Nebenwirkungen haben, sodass sie nicht so einfach optimiert werden können.

Zum Beispiel:

create function stable_function() returns int as $$
begin
  raise notice 'stable_function() called';
  return 1;
end
$$
language plpgsql stable;

create function volatile_function() returns int as $$
begin
  raise notice 'volatile_function() called';
  return 1;
end
$$
language plpgsql volatile;

create view v as
  select stable_function(), volatile_function();

Wenn nur die volatileSpalte ausgewählt ist:

test=# explain (analyse, verbose) select volatile_function from v;
NOTICE:  volatile_function() called
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Subquery Scan on v  (cost=0.00..0.27 rows=1 width=4) (actual time=0.057..0.057 rows=1 loops=1)
   Output: v.volatile_function
   ->  Result  (cost=0.00..0.26 rows=1 width=8) (actual time=0.056..0.056 rows=1 loops=1)
         Output: NULL::integer, volatile_function()

... dann, wie Sie sehen können, stable_function()fehlt in der explainAusgabe, und das Fehlen eines NOTICEbestätigt, dass dieser Aufruf weg optimiert wurde.

Wenn stablejedoch stattdessen die Spalte ausgewählt ist:

test=# explain (analyse, verbose) select stable_function from v;
NOTICE:  stable_function() called
NOTICE:  volatile_function() called
                                           QUERY PLAN
------------------------------------------------------------------------------------------------
 Subquery Scan on v  (cost=0.00..0.52 rows=1 width=4) (actual time=0.139..0.139 rows=1 loops=1)
   Output: v.stable_function
   ->  Result  (cost=0.00..0.51 rows=1 width=8) (actual time=0.138..0.138 rows=1 loops=1)
         Output: stable_function(), volatile_function()

... dann sehen wir beide Spaltenausdrücke im Plan und die NOTICEs zeigen, dass beide Funktionen ausgeführt wurden.

Es scheint keine explizite Erwähnung dieses Verhaltens in den Dokumenten zu geben, daher gibt es keine festen Garantien dafür, ob ein Ausdruck ausgewertet wird oder nicht, und Sie sollten sich nicht auf Nebenwirkungen verlassen, die Ihre Funktionsaufrufe haben könnten.

Wenn Ihre einzige Sorge jedoch die Leistung ist, können Sie, solange Sie Ihre Funktionen als stableoder immutablegegebenenfalls markieren , ziemlich sicher sein (insbesondere in einfachen Fällen wie diesen), dass sie nur bewertet werden, wenn sie benötigt werden.

(Und während Sie dort Ihre Volatilitätserklärungen prüfen, möchten Sie möglicherweise auch die parallelen Sicherheitsflags setzen .)

Nick Barnes
quelle
1
"so wird es unabhängig aufgerufen" Es wird gemäß den jeweiligen DBMS-Garantien aufgerufen. Davon mag es keine geben. Eine SQL-Abfrage beschreibt ein Ergebnis, keinen Prozess. PostgesSQL-Dokumentation zu VOLATILE: Der Optimierer macht keine Annahmen über das Verhalten solcher Funktionen. Bei einer Abfrage mit einer flüchtigen Funktion wird die Funktion in jeder Zeile neu bewertet, in der ihr Wert benötigt wird. (Was auch immer "benötigt" bedeutet.)
philipxy
@philipxy: Du hast absolut recht. Ich wollte keine Garantien implizieren, die über die in den Dokumenten angegebenen hinausgehen, obwohl ich dies beim erneuten Lesen sicherlich getan habe. Hoffentlich klärt meine Bearbeitung die Dinge. Vielen Dank!
Nick Barnes
4

Ihr Verdacht ist richtig und die Berechnung sollte nicht durchgeführt werden, wenn die Spalte nicht verwendet wird.

Um dies zu bestätigen, sehen Sie sich die Ausgabe von EXPLAIN (VERBOSE)für die Abfrage an, in der die zurückgegebenen Spalten angezeigt werden.

Laurenz Albe
quelle