2012-07-25 15 views
5

Nel post del blog di Bruce Momjian Generating Random Data Via SQL ha usato il seguente codice per generare 5 stringhe casuali:PostgreSQL espressioni volatili e subquery

SELECT 
(
     SELECT string_agg(x, '') 
     FROM (
       SELECT chr(ascii('a') + floor(random() * 26)::integer) 
       FROM generate_series(1, 40 + b * 0) as f(g) 
     ) AS y(x) 
) AS result 
FROM generate_series(1,5) as a(b); 

       result     
------------------------------------------ 
plwfwcgajxdygfissmxqsywcwiqptytjjppgrvgb 
sjaypirhuoynnvqjdgywfsfphuvzqbbilbhakyhf 
ngtabkjfqibwahlicgisijatliuwgbcuiwujgeox 
mqtnyewalettounachwjjzdrvxbbbpzogscexyfi 
dzcstpsvwpefohwkfxmhnlwteyybxejbdltwamsx 
(5 rows) 

mi sono chiesto perché 'b * 0' at line 6 è richiesto. Quando l'ho rimosso, il risultato è stato modificato in 5 stringhe esattamente simili, il che significa che Postgres ha memorizzato nella cache l'espressione di selezione esterna (risultato)!

Non sono riuscito a trovare il modo in cui la cache di espressione funziona in Postgres. Secondo the documentation la funzione random() è contrassegnata VOLATILE, quindi, mi aspetto che anche qualsiasi espressione dipenda da essa sia volatile.

Come funziona il caching delle espressioni in Postgres? È documentato da qualche parte? Perché 'b * 0' disabilita la cache dove random() no?

Aggiornamento:

per studiare la questione, mi sono trasferito 'b * 0' all'interno del piano() chiamata per essere alla stessa posizione/livello come casuale():

... 
       SELECT chr(ascii('a') + floor(random() * 26 + b * 0)::integer) 
       FROM generate_series(1, 40) as s(f) 
... 

Il risultato non è ancora stato memorizzato nella cache; stringhe diverse.

Update: Un altro esempio per mostrare il problema

create sequence seq_test; 

SELECT (SELECT nextval('seq_test')) FROM generate_series(1,5); 

?column? 
---------- 
     1 
     1 
     1 
     1 
     1 
(5 rows) 

risposta

4

Beh, random() è di per sé volatile, quindi non si ottiene stringhe con lo stesso carattere ripetuto fino alla fine.

Se si guardano i piani per le query con e senza b*0 si vedrà:

Con b*0:

Function Scan on generate_series a (cost=0.00..37530.00 rows=1000 width=4) 
    SubPlan 1 
    -> Aggregate (cost=37.51..37.52 rows=1 width=32) 
      -> Function Scan on generate_series (cost=0.01..25.01 rows=1000 width=0) 

Senza b*0:

Function Scan on generate_series a (cost=37.52..47.52 rows=1000 width=0) 
    InitPlan 1 (returns $0) 
    -> Aggregate (cost=37.50..37.51 rows=1 width=32) 
      -> Function Scan on generate_series (cost=0.00..25.00 rows=1000 width=0) 

Se PostgreSQL determina che il l'aggregazione interna non dipende da a, quindi viene valutata una volta come InitPlan e la volatilità o meno delle espressioni all'interno sono irrilevanti. Introducendo la dipendenza della subquery su a, ovvero rendendola una sottoquery correlata, la valutazione deve essere rifatta per ogni riga di a.

+0

Quindi, il planner ha valutato l'espressione interna per ogni riga 'f' nonostante non dipendesse da esso (correttamente perché è volatile), ma l'espressione esterna viene valutata una volta a meno che non dipenda dalla riga! Non dovrebbero i valori volatili tintare alcun risultato basato su di esso? proprio come faceva la dipendenza dalla riga. –

+0

la volatilità dell'espressione interromperà la sua inclinazione, ma non influisce sul modo in cui il pianificatore organizza il piano di query, che si basa sul fatto che la sottoselezione sia correlata o meno. – araqnid

+0

Vuoi dire che ogni subquery è considerata STABILE a meno che non dipenda dalla variabile di riga della query esterna! Mi sembra un insetto. Mi aspetto che le espressioni volatili abbiano un effetto virale di volatilità su qualsiasi espressione o sottoquery. InitPlan è un'ottimizzazione, non dovrebbe modificare il risultato. –