@alciの答えを詳しく説明するには:
PostgreSQLは、あなたが物事を書く順番を気にしません
PostgreSQLは、WHERE
句のエントリの順序をまったく考慮せず、コストと選択性の推定のみに基づいてインデックスと実行順序を選択します。
結合が書き込まれる順序も、構成されている限り無視されますjoin_collapse_limit
。それより多くの結合がある場合、記述された順序で実行します。
サブクエリは、外部クエリが実際に情報を必要とする前に実行される限り、最速のものに応じて、サブクエリを含むクエリの前または後に実行できます。多くの場合、サブクエリは途中で実行されるか、外部クエリとインターリーブされます。
PostgreSQLが実際にクエリの一部を実行するという保証はありません。それらは完全に最適化されます。これは、副作用のある関数を呼び出す場合に重要です。
PostgreSQLはクエリを変換します
PostgreSQLは、結果を変えずに実行を高速化するために、まったく同じ効果を保持しながらクエリを大幅に変換します。
サブクエリの外部の用語はサブクエリにプッシュダウンされるため、外部クエリで記述した場所ではなく、サブクエリの一部として実行されます。
サブクエリ内の用語は、外部クエリにプルアップできるため、サブクエリで記述した場所ではなく、外部クエリの一部として実行されます
サブクエリは、通常、外部テーブルの結合にフラット化できます。同じことがクエリEXISTS
やNOT EXISTS
クエリにも当てはまります。
ビューは、ビューを使用するクエリに統合されます
SQL関数は、呼び出し元のクエリにインライン化されることがよくあります
...また、定数式の事前評価、一部のサブクエリの非相関、その他のプランナー/オプティマイザトリックなど、クエリに対して行われる他の多数の変換があります。
一般に、PostgreSQLはクエリを大規模に変換および書き換えて、これらの各クエリを実行できます。
select my_table.*
from my_table
left join other_table on (my_table.id = other_table.my_table_id)
where other_table.id is null;
select *
from my_table
where not exists (
select 1
from other_table
where other_table.my_table_id = my_table.id
);
select *
from my_table
where my_table.id not in (
select my_table_id
from other_table
where my_table_id is not null
);
通常、すべてがまったく同じクエリプランを生成します。(とにかく上記で愚かな間違いをしなかったと仮定)。
クエリを最適化しようとするのは珍しいことではありません。クエリプランナーが試行しているトリックを既に見つけ出し、それらを自動的に適用しているため、手動で最適化したバージョンは元のバージョンよりも優れています。
制限事項
プランナー/オプティマイザーは、決して無名ではなく、クエリの効果を変更できないこと、決定に使用できるデータ、実装されているルール、CPU時間を絶対に変更できないという要件によって制限されます。最適化を熟考するのに費やす余裕があります。例えば:
プランナーはANALYZE
(通常は自動バキューム経由で)保持される統計情報に依存します。これらが古くなっている場合、計画の選択が悪い場合があります。
統計はサンプルにすぎないため、特にサンプルが少なすぎる場合は、サンプリング効果により誤解を招く可能性があります。計画の選択が不適切になる可能性があります。
統計は、列間の相関など、テーブルに関する特定の種類のデータを追跡しません。これにより、プランナーは、物事が独立していない場合に独立していると想定した場合に、悪い決定を下すことになります。
プランナーは、random_page_cost
インストールされている特定のシステムでのさまざまな操作の相対速度を伝えるなど、コストパラメーターに依存しています。これらは単なるガイドです。彼らがひどく間違っている場合、彼らは貧弱な計画の選択につながる可能性があります。
LIMIT
またはを含むサブクエリは、OFFSET
フラット化できないか、プルアップ/プッシュダウンの対象になりません。これは、しかし、あるいはそれが実行だろうと、外部クエリのすべての部分の前に実行しますという意味ではありませんまったく。
CTE用語(WITH
クエリ内の句)は、実行された場合、常に完全に実行されます。それらをフラット化することはできず、用語をCTEの用語バリアを超えてプッシュまたはプルダウンすることはできません。CTE用語は、常に最終クエリの前に実行されます。これは非SQL標準の動作ですが、PostgreSQLの動作として文書化されています。
PostgreSQLには、外部テーブル、security_barrier
ビュー、および特定の他の特別な種類のリレーションに対するクエリ全体を最適化する制限された機能があります
PostgreSQLは、プレーンSQL以外で記述された関数をインライン化しません。また、外部クエリとのプルアップ/プッシュダウンも行いません。
プランナー/オプティマイザーは、式のインデックスの選択、およびインデックスと式の間のデータタイプの些細な違いについて本当に愚かです。
トン以上。
あなたの質問
クエリの場合:
select 1
from workdays day
where day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30'
and day.offer_id in (
select offer.offer_day
from offer
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
)
追加の結合セットを使用した単純なクエリへのフラット化を妨げるものは何もありません。
おそらく次のようなものになるでしょう(明らかに、テストされていません):
select 1
from workdays day
inner join offer on day.offer_id = offer.offer_day
inner join province on offer.id_province = province.id_province
inner join center cr on cr.id_cr = province.id_cr
where upper(offer.code_status) <> 'A'
and province.id_region in ('10' ,'15' ,'21' ,'26' ,'31' , ...,'557')
and province.id_cr in ('9' ,'14' ,'20' ,'25' ,'30' ,'35' ,'37')
and day.date_day >= '2014-10-01'
and day.date_day <= '2015-09-30';
PostgreSQLは、選択性と行数の推定、および利用可能なインデックスに基づいて、結合順序と結合方法を最適化します。これらが現実を合理的に反映している場合、結合を実行し、最適な順序でwhere句エントリを実行します-多くの場合、それらを一緒に混合するので、これを少し実行してから、最初の部分に戻りますなど
オプティマイザーが何をしたかを見る方法
PostgreSQLがクエリを最適化するSQLを見ることができません。SQLを内部クエリツリー表現に変換してから変更するためです。あなたはすることができますクエリプランをダンプし、他のクエリと比較します。
そのクエリプランまたは内部プランツリーをSQLに「逆解析」する方法はありません。
http://explain.depesz.com/には、適切なクエリプランヘルパーがあります。クエリプランなどにまったく慣れていない場合(この場合、この記事を読んで驚くことになります)、PgAdminにはグラフィカルなクエリプランビューアーがあり、情報ははるかに少なくなりますが簡単です。
関連資料:
プッシュダウン/プルアップおよびフラット化機能は、各リリースで引き続き改善されます。PostgreSQLは通常、プルアップ/プッシュダウン/フラット化の決定について正しいですが、常にではないため、CTEまたはOFFSET 0
ハックを(ab)使用しなければならない場合があります。そのような場合は、クエリプランナーのバグを報告してください。
もしあなたが本当に本当に熱心なら、debug_print_plans
オプションを使って生のクエリプランを見ることができますが、私はそれを読みたくないと約束します。本当に。