PG Day'16カンファレンスは日々近づいてきており、 Hubert Lubaczewskiによる一連の記事の分析とその主要な操作についての公開を続けています。これ
で、 シリーズの 最後から2番目の 投稿で、explain出力で見つけることができる残りの最も一般的な操作についてお話したいと思います。

ユニーク
操作の名前はそれ自身を表しています-重複データを削除します。
これは、たとえば次のことを行うときに発生する可能性があります。
select distinct field from table
Postgresの最新バージョンでは、このリクエストはHashAggregateを使用して実装されます。
Uniqueの問題は、そのデータをソートする必要があることです。 この操作には特定の順序のデータが必要なためではなく、同じ値を持つすべての行が「一緒に」なるようにするためです。
これにより、Uniqueは実際にメモリを必要としないため、(使用できる場合に)本当にクールな操作になります。 前の行の値と現在の値を単純に比較し、同じ場合は破棄します。 以上です。
したがって、データを事前に並べ替えることで、その使用を促進できます。
$ explain select distinct relkind from (select relkind from pg_class order by relkind) as x; QUERY PLAN
追記
このプランは、単純に多くのサブオペレーションを起動し、それらによって返されるすべての行を一般的な結果として返します。
これは、UNION / UNION ALLクエリで使用されます。
$ explain select oid from pg_class union all select oid from pg_proc union all select oid from pg_database; QUERY PLAN
ここでは、appendが3つのテーブルで3つのスキャンを実行し、すべての行をまとめて返す方法を示しています。
UNION ALLを使用したことに注意してください。 UNIONを使用すると、次の結果が得られます。
$ explain select oid from pg_class union select oid from pg_proc union select oid from pg_database; QUERY PLAN
これは、UNIONが重複行を削除するためです。この場合、HashAggregate操作によって行われました。
結果
結果は主に非常に単純なテストクエリに表示されます。 この操作は、クエリがいくつかの定数値を選択するときに使用されます:
$ explain select 1, 2; QUERY PLAN
クエリのテストに加えて、「挿入しますが、これがデータの複製ではない場合のみ」のようなことを行うクエリで見つけることができます。
$ explain insert into t (i) select 1 where not exists (select * from t where i = 1); QUERY PLAN
値スキャン
結果と同様に、Values Scanはクエリに入力された単純なデータを返すために使用されますが、この場合はVALUES()機能に基づいたレコードのセット全体である場合があります。
突然最新ではない場合は、次の例のように、VALUES構文を使用するだけで、テーブルなしで多くの行と列を選択できます。
$ select * from ( values (1, 'hubert'), (2, 'depesz'), (3, 'lubaczewski') ) as t (a,b); a | b
このような要求の計画は次のとおりです。
QUERY PLAN
この操作は通常INSERTで使用されますが、
カスタムソートなどの他の用途もあります。
GroupAggregate
この操作は、前に説明したHashAggregateに似て
います。
違いは、GroupAggregateが機能するために、GROUP BY句で使用した1つまたは複数の列を使用してデータを並べ替える必要があることです。
Uniqueと同様に、GroupAggregateはメモリをほとんど使用しませんが、データの合理化が必要です。
例:
$ explain select relkind, count(*) from (select relkind from pg_class order by relkind) x group by relkind; QUERY PLAN
ハッシュセトップ
この操作は、INTERSECT / EXCEPT操作で使用されます(オプションの修飾子「ALL」を使用)。
次のように機能します。サブクエリのペアに対してサブオペレーションを追加し、結果とオプションのALL修飾子に基づいて、返す行を決定します。 私はソースコードに深く入りませんでしたので、それがどのように機能するかを正確に言うことはできませんが、名前に基づいて、操作はカウンターに基づく単純なソリューションに似ています。
UNIONとは異なり、これらの操作は2つのデータソースで機能することがわかります。
$ explain select * from (select oid from pg_Class order by oid) x intersect all select * from (select oid from pg_proc order by oid) y; QUERY PLAN
そして、3つのソースを使用して、より複雑なツリーを取得します。
$ explain select * from (select oid from pg_Class order by oid) x intersect all select * from (select oid from pg_proc order by oid) y intersect all select * from (Select oid from pg_database order by oid) as w; QUERY PLAN
Cteスキャン
この操作は、
すでに説明したマテリアライズ操作に似て
います。 要求の一部を起動し、その出力を保存して、要求の他の部分で使用できるようにします。
例:
$ explain analyze with x as (select relname, relkind from pg_class) select relkind, count(*), (select count(*) from x) from x group by relkind; QUERY PLAN
pg_classは1回だけスキャンされることに注意してください-6行目。 ただし、その結果は「x」に保存され、2回スキャンされます-集計内(行番号9)とHashAggregate操作(10)内。
マテリアライズとの違いは何ですか? この質問に対する詳細な回答を得るには、ソースコードを詳しく調べる必要がありますが、違いは、CTEがユーザー定義であるという単純な事実に基づいていると言えます。一方、Materializeは、Postgresが適切と判断したときに使用することを決定する補助操作です。 。
重要な注意:CTEは常に指示どおりに開始されます。 したがって、スケジューラーが実装できる最適な最適化ではなく、回避するために使用できます。
初期計画
この計画は、他のすべての前に計算できる(またはする必要がある)リクエストの一部があるたびに発生し、リクエストの残りの部分に依存しません。
このクエリが必要だとしましょう:
$ explain select * from pg_class where relkind = (select relkind from pg_class order by random() limit 1); QUERY PLAN
この場合、Postgresはrelkindの値とサブクエリによって返された値を比較する必要があるため、pg_classを使用して通常の順次スキャンの前にlimit / sort / seq-scanを実行する必要があります。
一方、次のように書くこともできます。
$ explain select *, (select length('depesz')) from pg_class; QUERY PLAN
Postgresは、副選択列がpg_classテーブルのデータに依存しないことを正しく認識しているため、一度実行すれば行ごとに長さを再計算する必要はありません。
もちろん、次のように、多くの単一の計画(初期計画)を持つことができます。
$ explain select *, (select length('depesz')) from pg_class where relkind = (select relkind from pg_class order by random() limit 1); QUERY PLAN
ただし、1つの詳細を検討する価値があります。1つのリクエスト内の初期化プランには、運用ではなく「グローバル」に番号が付けられます。
サブプラン
SubPlanは、NestedLoopに多少似ています。 何度も呼び出せるという意味で。
SubPlanは、現在の行に実際に依存するサブクエリからデータを計算するために呼び出されます。
例:
$ explain analyze select c.relname, c.relkind, (Select count(*) from pg_Class x where c.relkind = x.relkind) from pg_Class c; QUERY PLAN
「pg_class as c」をスキャンして返された各行に対して、PostgresはSubPlanを起動し、pg_classの行数がrelkind列の値(処理されたばかりの行)と同じかどうかを確認します。
「pg_class xでのSeqスキャン」行の「loops = 295」と、ノード「pg_class cでのSeqスキャン」の対応する値「rows = 295」に注意してください。
その他?
はい、他の操作があります。 それらのいくつかは私たちの注目に値するにはあまりにもまれです(特にあなたが素晴らしい知識のソースを持っていると考える場合-ソースコード)、そしていくつかは新しいノードの古いバージョンです(私は疑うように)。
私が説明しなかった操作の計画があり、理解できない場合は、説明にリンクを記述して、
explain.depesz.com 、操作の名前、およびあなたが会ったPostgresのバージョンを説明してください。 そのような場合に考えられるすべての情報を見つけて、詳細な回答をお届けします。