Postgres eseguirà il calcolo delle colonne calcolate che non sono selezionate in una vista?


8

Sto cercando di comprendere l'impatto delle prestazioni della selezione dei dati da una vista, in cui una delle colonne in una vista è funzione di altri dati nella tabella originale.

Il calcolo viene eseguito indipendentemente dal fatto che la colonna calcolata sia o meno nell'elenco delle colonne selezionate?

Se avessi un tavolo e la vista dichiarasse così

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

Sarebbe che la moltiplicazione essere eseguita in una query come quella qui sotto?

select ticker, ddate, price, factor from prices

C'è un riferimento che garantisce questo in un modo o nell'altro? Stavo leggendo la documentazione sul sistema di regole in Postgres, ma penso che la risposta sia proprio dell'ottimizzatore, dal momento che nulla nella documentazione del sistema di regole indicava che non sarebbe stato selezionato.

Sospetto nel caso precedente che il calcolo non sia eseguito. Ho cambiato la vista per usare la divisione invece della moltiplicazione e ho inserito un 0for factorin price_data. La query sopra non è fallita, ma se la query è stata modificata per selezionare la colonna calcolata, la query modificata non è riuscita.

Esiste un modo per capire quali calcoli vengono eseguiti quando selectviene eseguita una? Immagino che sto cercando qualcosa di simile, EXPLAINma che mi dice anche del calcolo (s) che vengono eseguiti.


1
Questa è un'ottima domanda del tipo che vogliamo incoraggiare su questo SE
Gaius,

Risposte:


6

Come ha detto @Laurenz, la tua analisi è corretta: l'ottimizzatore eviterà di valutare le espressioni di colonna che non influenzano il risultato della query (e il tuo tentativo di forzare un errore di divisione per zero ne è la prova).

Ciò dipende dalle colonne selezionate, ma dipende anche dalla categoria di volatilità delle espressioni di colonna. L'ottimizzatore è libero di omettere immutablee stablechiamare le funzioni se il loro output non viene mai utilizzato, in quanto non possono influenzare il risultato, ma le volatilefunzioni potrebbero avere effetti collaterali, quindi non sono così facilmente ottimizzate.

Per esempio:

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();

Se volatileè selezionata solo la colonna:

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()

... quindi, come puoi vedere, stable_function()è assente explaindall'output e la mancanza di un NOTICEconferma che questa chiamata è stata ottimizzata.

Tuttavia, se stableinvece è selezionata la colonna:

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()

... poi vediamo entrambe le espressioni di colonna che appaiono nel piano e le NOTICEs mostrano che entrambe le funzioni sono state eseguite.

Non sembra esserci alcuna menzione esplicita di questo comportamento nei documenti, quindi non ci sono garanzie concrete sulla valutazione o meno di un'espressione e non dovresti fare affidamento sugli effetti collaterali che le tue chiamate di funzione potrebbero avere.

Ma se la tua unica preoccupazione è la prestazione, fintanto che contrassegni le tue funzioni come stableo immutabledove appropriato, puoi essere ragionevolmente sicuro (specialmente in casi semplici come questo) che non saranno valutate a meno che non siano necessarie.

(E mentre sei lì a controllare le dichiarazioni di volatilità, potresti voler impostare anche i contrassegni di sicurezza paralleli .)


1
"così verrà chiamato indipendentemente" Verrà chiamato in base alle garanzie del DBMS particolare. Di cui potrebbe non essercene nessuno. Una query SQL descrive un risultato, non un processo. Documentazione di PostgesSQL su VOLATILE: L'ottimizzatore non fa ipotesi sul comportamento di tali funzioni. Una query che utilizza una funzione volatile rivaluterà la funzione in ogni riga in cui è necessario il suo valore. (Qualunque cosa significhi "necessario").
philipxy,

@philipxy: hai assolutamente ragione. Non intendevo implicare alcuna garanzia oltre a quelle dichiarate nei documenti, anche se sulla rilettura, l'ho sicuramente fatto. Spero che la mia modifica chiarisca le cose. Grazie!
Nick Barnes,

4

Il tuo sospetto è corretto e il calcolo non dovrebbe essere eseguito se la colonna non viene utilizzata.

Per confermare ciò, guarda l'output di EXPLAIN (VERBOSE)per la query, che ti mostrerà le colonne restituite.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.