編集:
謝罪して、受け入れられた答えが常に正しいとは限らないという主張を撤回する必要があります-それは、ビューが常にサブクエリとして書かれたものと同じであると述べています。それは議論の余地のないことだと思うし、私の場合は何が起こっているのかがわかったと思う。
私は今、元の質問に対するより良い答えがあると思います。
元の質問は、ビューを使用するプラクティスをガイドする必要があるかどうかです(たとえば、2回以上保守する必要があるルーチンでSQLを繰り返すのとは対照的です)。
私の答えは、「クエリがウィンドウ関数またはサブクエリになるときにオプティマイザがクエリを異なる方法で処理する他のものを使用する場合ではありません。サブクエリを作成する行為(ビューとして表されるかどうか)実行時にパラメータでフィルタリングする場合。
私の窓関数の複雑さは不要です。これの説明計画:
SELECT DISTINCT ts.train_service_key,
pc.assembly_key,
count(*) OVER
(PARTITION BY ts.train_service_key) AS train_records
FROM staging.train_service ts
JOIN staging.portion_consist pc
USING (ds_code, train_service_key)
WHERE assembly_key = '185132';
これよりもはるかに安価です:
SELECT *
FROM (SELECT DISTINCT ts.train_service_key,
pc.assembly_key,
count(*) OVER
(PARTITION BY ts.train_service_key) AS train_records
FROM staging.train_service ts
JOIN staging.portion_consist pc
USING (ds_code, train_service_key)) AS query
WHERE assembly_key = '185132';
それがもう少し具体的で役立つことを願っています。
私の最近の経験(この質問を見つけるきっかけ)で、上記の受け入れられた答えはすべての状況下で正しくありません。ウィンドウ関数を含む比較的単純なクエリがあります:
SELECT DISTINCT ts.train_service_key,
pc.assembly_key,
dense_rank() OVER (PARTITION BY ts.train_service_key
ORDER BY pc.through_idx DESC, pc.first_portion ASC,
((CASE WHEN (NOT ts.primary_direction)
THEN '-1' :: INTEGER
ELSE 1
END) * pc.first_seq)) AS coach_block_idx
FROM (staging.train_service ts
JOIN staging.portion_consist pc USING (ds_code, train_service_key))
このフィルターを追加する場合:
where assembly_key = '185132'
私が得る説明計画は次のとおりです。
QUERY PLAN
Unique (cost=11562.66..11568.77 rows=814 width=43)
-> Sort (cost=11562.66..11564.70 rows=814 width=43)
Sort Key: ts.train_service_key, (dense_rank() OVER (?))
-> WindowAgg (cost=11500.92..11523.31 rows=814 width=43)
-> Sort (cost=11500.92..11502.96 rows=814 width=35)
Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
-> Nested Loop (cost=20.39..11461.57 rows=814 width=35)
-> Bitmap Heap Scan on portion_consist pc (cost=19.97..3370.39 rows=973 width=38)
Recheck Cond: (assembly_key = '185132'::text)
-> Bitmap Index Scan on portion_consist_assembly_key_index (cost=0.00..19.72 rows=973 width=0)
Index Cond: (assembly_key = '185132'::text)
-> Index Scan using train_service_pk on train_service ts (cost=0.43..8.30 rows=1 width=21)
Index Cond: ((ds_code = pc.ds_code) AND (train_service_key = pc.train_service_key))
これは、trainサービステーブルの主キーインデックスと、portion_consistテーブルの一意でないインデックスを使用しています。90msで実行されます。
ビューを作成しました(完全に明確にするためにここに貼り付けていますが、文字通りビュー内のクエリです)。
CREATE OR REPLACE VIEW staging.v_unit_coach_block AS
SELECT DISTINCT ts.train_service_key,
pc.assembly_key,
dense_rank() OVER (PARTITION BY ts.train_service_key
ORDER BY pc.through_idx DESC, pc.first_portion ASC, (
(CASE
WHEN (NOT ts.primary_direction)
THEN '-1' :: INTEGER
ELSE 1
END) * pc.first_seq)) AS coach_block_idx
FROM (staging.train_service ts
JOIN staging.portion_consist pc USING (ds_code, train_service_key))
同一のフィルターでこのビューを照会すると:
select * from staging.v_unit_coach_block
where assembly_key = '185132';
これは説明計画です。
QUERY PLAN
Subquery Scan on v_unit_coach_block (cost=494217.13..508955.10 rows=3275 width=31)
Filter: (v_unit_coach_block.assembly_key = '185132'::text)
-> Unique (cost=494217.13..500767.34 rows=655021 width=43)
-> Sort (cost=494217.13..495854.68 rows=655021 width=43)
Sort Key: ts.train_service_key, pc.assembly_key, (dense_rank() OVER (?))
-> WindowAgg (cost=392772.16..410785.23 rows=655021 width=43)
-> Sort (cost=392772.16..394409.71 rows=655021 width=35)
Sort Key: ts.train_service_key, pc.through_idx DESC, pc.first_portion, ((CASE WHEN (NOT ts.primary_direction) THEN '-1'::integer ELSE 1 END * pc.first_seq))
-> Hash Join (cost=89947.40..311580.26 rows=655021 width=35)
Hash Cond: ((pc.ds_code = ts.ds_code) AND (pc.train_service_key = ts.train_service_key))
-> Seq Scan on portion_consist pc (cost=0.00..39867.86 rows=782786 width=38)
-> Hash (cost=65935.36..65935.36 rows=1151136 width=21)
-> Seq Scan on train_service ts (cost=0.00..65935.36 rows=1151136 width=21)
これは両方のテーブルでフルスキャンを実行しており、17秒かかります。
これに出くわすまで、私はPostgreSQLでビューを自由に使用してきました(受け入れられた回答で表明されている広く見られるビューを理解していました)。事前集計フィルタリングが必要な場合は、セットを返す関数を使用するため、特にビューの使用を避けます。
また、PostgreSQLのCTEは設計上、厳密に個別に評価されるため、SQL Serverの場合と同じようには使用しません。たとえば、サブクエリとして最適化されているようです。
したがって、私の答えは、ビューが基になっているクエリと正確に一致しない場合があるため、注意することをお勧めします。PostgreSQL 9.6.6に基づいたAmazon Auroraを使用しています。
SELECT * FROM my_view WHERE my_column = 'blablabla';
その後フィルターを適用します。2つ目はビューを使用して、それを使用するアプリケーションに対してデータモデルを透過的にすることです。最初のソースは、WHERE my_column = 'blablabla'
ビュー定義内にフィルターを含めるように指示します。これにより、実行計画が改善されます。