web-dev-qa-db-ja.com

Postgresはビューで選択されていない計算された列の計算を実行しますか?

ビューからデータを選択することによるパフォーマンスへの影響を理解しようとしています。ビューの列の1つは、元のテーブルの他のデータの関数です。

計算された列が選択した列のリストにあるかどうかに関係なく、計算は実行されますか?

テーブルがあり、ビューがそのように宣言されている場合

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

that乗算は以下のようなクエリで実行されますか?

select ticker, ddate, price, factor from prices

これを保証するリファレンスはありますか?私はPostgresのルールシステムのドキュメントを読んでいましたが、ルールシステムのドキュメントには何も選択されないことを示していないため、答えは本当にオプティマイザにあると思います。

上記のケースでは、計算が実行されていないと思われます。乗算ではなく除算を使用するようにビューを変更し、factor0price_dataに挿入しました。上記のクエリは失敗しませんでしたが、計算列を選択するようにクエリが変更された場合、変更されたクエリは失敗しました。

selectが実行されたときに実行されている計算を理解する方法はありますか? EXPLAINのようなものを探していると思いますが、これは実行されている計算についても教えてくれます。

8
Varun Madiath

@Laurenzが言ったように、あなたの分析は正しいです:オプティマイザーはクエリの結果に影響を与えない列式の評価を避けます(そしてゼロ除算エラーを強制しようとするあなたの試みはこれの証拠です)。

これは、選択する列によって異なりますが、列式の ボラティリティカテゴリ にも依存します。オプティマイザは、結果に影響を与えることができないため、出力が使用されない場合、immutableおよびstable関数呼び出しを自由に省略できますが、volatile関数には副作用がある可能性があるため、それらはすぐには最適化されません。

例えば:

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

volatile列のみが選択されている場合:

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

...ご覧のとおり、explainの出力にはstable_function()がなく、NOTICEがないため、この呼び出しが最適化されていることがわかります。

ただし、代わりにstable列が選択されている場合:

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

...次に、プランに両方の列式が表示され、NOTICEsは両方の関数が実行されたことを示しています。

ドキュメントではこの動作について明示的に言及されていないようです。そのため、式が評価されるかどうかについて厳密な保証はありません。また、関数呼び出しがもたらす可能性のある副作用に依存すべきではありません。

ただし、パフォーマンスのみが問題である場合は、関数をstableまたはimmutableとして適切にマークしている限り、(特に、このような単純なケースでは)機能しないことを合理的に確信できます。必要がない限り評価されます。

(そして、あなたがボラティリティ宣言を監査している間、 並列安全 フラグも設定したいかもしれません。)

6
Nick Barnes

あなたの疑いは正しいです、そして列が使用されていない場合は計算を実行すべきではありません。

これを確認するには、クエリのEXPLAIN (VERBOSE)の出力を見てください。返された列が表示されます。

4
Laurenz Albe