これは、Postgresql(9.5または9.6)の高価なJOINまたはサブクエリの短絡に関する質問です。また、一般的な人々がチェック・ザ・エグゼキュートの問題をどのように解決するかを聞くのにも興味があります。
(Web)ユーザーがレコードを所有しているか、レコードが変更されているかなど、条件付きで結果を返すだけのクエリをたくさん書いています。 Postgresql内で高価なビューを構築したり、アプリケーション自体の条件を確認するために複数の前後のクエリを作成したりするのを防ぐため、最初に正しいレコードを選択し、どの条件が失敗したかを示すクエリを記述して、条件が満たされているかどうかを確認します。
たとえば、これは、(アプリケーション)ユーザーがレコードを所有するかどうかをチェックしてから、それを返します。
SELECT is_owner, is_newer, json
FROM (
SELECT id, owner = '053bffbc-c41e-dad4-853b-ea91fc42ea18' "is_owner"
, modified >= created "is_newer"
FROM datasets
WHERE id = '056e4eed-ee63-2add-e981-0c86b8b6a66f'
) cond
LEFT JOIN LATERAL (
SELECT id
FROM datasets
WHERE is_owner and is_newer
) authed
ON cond.id = authed.id
LEFT JOIN LATERAL (
SELECT json
FROM view_dataset
WHERE id = authed.id
) dataset
ON true;
結果は(所有者です):
is_owner | is_newer | json
t t {...}
そして否定的な結果(所有者ではない):
is_owner | is_newer | json
f t NULL
そのため、アプリケーションはどのエラーを返すかを認識していますが、条件が満たされない場合にビューを構築または解析する必要はありません。
ただし、EXPLAIN ANALYZEは中央のJOINに結果がない場合でも、最後のLEFT LATERAL JOINでビュークエリを実行しますを示し、短絡を防止して、 (高価)view_dataset SELECTが実行されていません。 json
をnull
に設定すると、クエリは最初のSELECT以外のすべてをスキップします。しかし、それが最後のクエリの値に設定されている場合、常にすべてのSELECTを実行するので、クエリプランナーは、最上位のSELECTクエリのそのjson
フィールドの結果を取得する必要があると考え、 t JOINを短絡します。
高価なビュークエリをPostgresqlに強制的にドロップさせることができるかどうか疑問に思います。
また、JOINクエリをスキップするように見えるCTEも試しました。
WITH cond as (
SELECT id, owner = '053bffbc-c41e-dad4-853b-ea91fc42ea18' "is_owner", modified >= created "is_newer" FROM datasets WHERE id = '056e4eed-ee63-2add-e981-0c86b8b6a66f'
)
SELECT cond.id, cond.is_owner, cond.is_newer, json FROM
(SELECT id FROM cond WHERE cond.is_owner and cond.is_newer) filtered
LEFT JOIN LATERAL
(SELECT id, json from view_dataset) dataset
USING (id)
RIGHT JOIN cond
USING(id);
...しかし、このクエリとバリエーションは少なくとも2倍遅くなります。
したがって、私の質問は、条件に基づいてJOINまたはサブクエリを短絡してパフォーマンスを最大化する方法です。また、レコードの所有権の確認など、最初に実行してから実行するチェックのパターンを実装する方法が他にあるかどうかを知りたいと思っています。
authed
が必要な理由がわかりません。何が:
SELECT is_owner, is_newer, json
FROM (
SELECT id, owner = '053bffbc-c41e-dad4-853b-ea91fc42ea18' "is_owner"
, modified >= created "is_newer"
FROM datasets
WHERE id = '056e4eed-ee63-2add-e981-0c86b8b6a66f'
) cond
LEFT JOIN LATERAL (
SELECT json
FROM view_dataset
WHERE id = cond.id
) dataset
ON is_owner and is_newer;
あなたによって? a_horse_with_no_name によるコメントにも同意します。 LATERALは、特別な場合に述語をベーステーブルにプッシュするのに非常に役立ちますが、変装したサブクエリにすぎないため、ほとんどの場合、通常の結合を行う方が理にかなっています。また試してください:
SELECT is_owner, is_newer, json
FROM (
SELECT id, owner = '053bffbc-c41e-dad4-853b-ea91fc42ea18' "is_owner"
, modified >= created "is_newer"
FROM datasets
WHERE id = '056e4eed-ee63-2add-e981-0c86b8b6a66f'
) cond
LEFT JOIN view_dataset wd
ON wd.id = cond.id
AND cond.is_owner
AND cond.is_newer;
編集。テーブル値関数
CREATE FUNCTION get_view_dataset(int,bool)
RETURNS setof view_dataset AS '
SELECT *
FROM view_dataset wd
WHERE wd.id = $1
AND $2;
' LANGUAGE SQL;
次に、クエリでその関数を次のように使用します。
SELECT is_owner, is_newer, json
FROM (
SELECT id, owner = '053bffbc-c41e-dad4-853b-ea91fc42ea18' "is_owner"
, modified >= created "is_newer"
FROM datasets
WHERE id = '056e4eed-ee63-2add-e981-0c86b8b6a66f'
) cond
LEFT JOIN get_view_dataset(cond.id, cond.is_owner AND cond.is_newer);
すべて未テスト。