Akankah Postgres melakukan perhitungan kolom terhitung yang tidak dipilih dalam tampilan?

8

Saya mencoba memahami dampak kinerja memilih data dari tampilan, di mana salah satu kolom dalam tampilan adalah fungsi dari data lain dalam tabel asli.

Apakah perhitungan dilakukan terlepas dari apakah kolom yang dihitung ada dalam daftar kolom yang dipilih atau tidak?

Jika saya punya meja dan tampilan dinyatakan seperti itu

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

Akankah bahwa perkalian dilakukan di query seperti di bawah?

select ticker, ddate, price, factor from prices

Apakah ada referensi yang menjamin cara ini atau yang lain? Saya membaca dokumentasi tentang sistem aturan di Postgres, tetapi saya pikir jawabannya benar-benar terletak pada optimiser, karena tidak ada dalam dokumentasi sistem aturan yang menunjukkan bahwa itu tidak akan dipilih.

Saya menduga dalam kasus di atas perhitungan tidak dilakukan. Saya mengubah pandangan ke divisi penggunaan bukan perkalian, dan memasukkan 0untuk factormenjadi price_data. Kueri di atas tidak gagal, tetapi jika kueri dimodifikasi untuk memilih kolom yang dihitung kueri yang diubah gagal.

Apakah ada cara untuk memahami perhitungan apa yang sedang dilakukan ketika a selectdilakukan? Saya kira saya sedang mencari sesuatu seperti EXPLAINtetapi yang juga memberitahu saya tentang perhitungan yang dilakukan.

Varun Madiath
sumber
1
Ini adalah pertanyaan luar biasa dari jenis yang ingin kami dorong dalam SE
Gayus

Jawaban:

6

Seperti yang dikatakan @Laurenz, analisis Anda benar: pengoptimal akan menghindari mengevaluasi ekspresi kolom yang tidak memengaruhi hasil kueri (dan upaya Anda untuk memaksakan kesalahan divide-by-zero adalah buktinya).

Ini tergantung pada kolom mana yang Anda pilih, tetapi juga tergantung pada kategori volatilitas dari ekspresi kolom. Pengoptimal bebas untuk dihilangkan immutabledan stablepanggilan fungsi jika output mereka tidak pernah digunakan, karena mereka tidak dapat mempengaruhi hasil, tetapi volatilefungsi mungkin memiliki efek samping, sehingga mereka tidak begitu mudah dioptimalkan.

Sebagai contoh:

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

Jika hanya volatilekolom yang dipilih:

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

... maka seperti yang Anda lihat, stable_function()tidak ada dalam explainoutput, dan tidak adanya NOTICEkonfirmasi bahwa panggilan ini telah dioptimalkan.

Namun, jika stablekolom yang dipilih sebagai gantinya:

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

... lalu kita melihat kedua ekspresi kolom muncul dalam rencana, dan NOTICEs menunjukkan bahwa kedua fungsi telah dieksekusi.

Tampaknya tidak ada penyebutan eksplisit perilaku ini dalam dokumen, jadi tidak ada jaminan keras mengenai apakah ekspresi akan dievaluasi, dan Anda tidak boleh mengandalkan efek samping apa pun yang mungkin dimiliki oleh panggilan fungsi Anda.

Tetapi jika satu-satunya masalah Anda adalah kinerja, maka selama Anda menandai fungsi Anda sebagai stableatau di immutablemana sesuai, Anda dapat yakin (terutama dalam kasus-kasus sederhana seperti ini) bahwa mereka tidak akan dievaluasi kecuali mereka diperlukan.

(Dan saat Anda di sana mengaudit deklarasi volatilitas Anda, Anda mungkin ingin mengatur bendera keselamatan paralel juga.)

Nick Barnes
sumber
1
"jadi itu akan dipanggil terlepas" Itu akan dipanggil sesuai dengan jaminan DBMS tertentu. Yang mungkin tidak ada. Kueri SQL menjelaskan hasil, bukan proses. Dokumentasi PostgesSQL kembali VOLATILE: Pengoptimal tidak membuat asumsi tentang perilaku fungsi tersebut. Kueri yang menggunakan fungsi volatil akan mengevaluasi kembali fungsi di setiap baris di mana nilainya diperlukan. (Apa pun yang "dibutuhkan" berarti.)
philipxy
@ philipxy: Anda memang benar. Saya tidak bermaksud menyiratkan jaminan apa pun di luar yang dinyatakan dalam dokumen, meskipun saat membaca ulang, saya tentu saja melakukannya. Semoga hasil edit saya mengklarifikasi hal-hal. Terima kasih!
Nick Barnes
4

Kecurigaan Anda benar, dan perhitungan tidak boleh dilakukan jika kolom tidak digunakan.

Untuk mengonfirmasi itu, lihat output EXPLAIN (VERBOSE)untuk kueri, yang akan menampilkan kolom yang dikembalikan.

Laurenz Albe
sumber