私の質問は:
SELECT Acol1, Acol2, Bcol1, Bcol2, Ccol1, Ccol2
FROM tableA LEFT JOIN
(tableB FULL JOIN tableC ON (Bcol1 = Ccol1))
ON (Acol1 = Bcol1)
EXPLAIN ANALYZE
私に与える:
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Hash Right Join (cost=99.65..180.45 rows=1770 width=24) (actual time=0.043..0.103 rows=3 loops=1)
Hash Cond: (tableb.bcol1 = tablea.acol1)
-> Hash Left Join (cost=49.83..104.08 rows=1770 width=16) (actual time=0.011..0.062 rows=3 loops=1)
Hash Cond: (tableb.bcol1 = tablec.ccol1)
-> Seq Scan on tableb (cost=0.00..27.70 rows=1770 width=8) (actual time=0.001..0.002 rows=3 loops=1)
-> Hash (cost=27.70..27.70 rows=1770 width=8) (actual time=0.004..0.004 rows=3 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 1kB
-> Seq Scan on tablec (cost=0.00..27.70 rows=1770 width=8) (actual time=0.001..0.002 rows=3 loops=1)
-> Hash (cost=27.70..27.70 rows=1770 width=8) (actual time=0.014..0.014 rows=3 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 1kB
-> Seq Scan on tablea (cost=0.00..27.70 rows=1770 width=8) (actual time=0.009..0.011 rows=3 loops=1)
Total runtime: 0.151 ms
PostgresはfulltableB
とtableC
の間の外部結合をright outerjoin。後でtableA
を使用した左側のouterjoinはとにかくnull値を削除するためです。元のクエリと同等です。
ただし、Postgresをハッキングして、結合列挙関連のアルゴリズムを実装し、実験を行っています。 Postgresが完全なouterjoinを左のouterjoinに変更することを望まない。そうする方法はありますか?
目的に応じて最適化バリアを導入できます。
この質問の目的には、プレーンなEXPLAIN
(ANALYZE
なし)で十分です。 ON
式を囲む括弧は単なるノイズです。明確にするためにテーブルエイリアスを追加します。
完全結合自体については、「完全結合」が表示されます。
SELECT * FROM tableB b FULL JOIN tableC c ON b.Bcol1 = c.Ccol1;
サブクエリを使用して完全結合を書き換えることができます。
SELECT a.Acol1, a.Acol2, d.Bcol1, d.Bcol2, d.Ccol1, d.Ccol2
FROM tableA a
LEFT JOIN (
SELECT * -- sort out conflicting names with aliases
FROM tableB b FULL JOIN tableC c ON b.Bcol1 = c.Ccol1
) d ON a.Acol1 = d.Bcol1;
サブクエリでエイリアスと競合する名前を整理する必要がありますが、その場合も、基になるテーブルの同じ名前の複数の列の外側のSELECT
でこれを行う必要があります。
サブクエリは最適化の障壁を課さないため、クエリは全体として最適化されます。 「左結合」または「右結合」が引き続き表示されます)。ただし、このフォームを拡張して解決策を見つけることができます。
OFFSET 0
ハック(文書化されていない)EXPLAIN
SELECT a.Acol1, a.Acol2, d.Bcol1, d.Bcol2, d.Ccol1, d.Ccol2
FROM tableA a
LEFT JOIN (
SELECT * -- you'll have to sort out conflicting names with aliases
FROM tableB b FULL JOIN tableC c ON b.Bcol1 = c.Ccol1
OFFSET 0 -- undocumented hack
) d ON a.Acol1 = d.Bcol1;
「完全結合」が表示されます。
なぜですか?サブクエリがOFFSET
句を使用するとすぐに、クエリプランナー/オプティマイザーはサブクエリを個別に計画します。 OFFSET 0
は論理的なノイズですが、Postgresは、これをクエリのヒントにしてサブクエリを効果的に実現する句を考慮しています。 (Postgresは他の方法ではクエリヒントをサポートしていませんが。)これは多くの議論の的となっている問題です。関連:
EXPLAIN
WITH cte AS (
SELECT * -- you'll have to sort out conflicting names with aliases
FROM tableB b FULL JOIN tableC c ON b.Bcol1 = c.Ccol1
)
SELECT a.Acol1, a.Acol2, d.Bcol1, d.Bcol2, d.Ccol1, d.Ccol2
FROM tableA a
LEFT JOIN cte d ON a.Acol1 = d.Bcol1;
「完全結合」も表示されます。
WITH
クエリの便利なプロパティは、親クエリまたは兄弟のWITH
クエリによって複数回参照されている場合でも、親クエリの実行ごとに1回だけ評価されることです。したがって、複数の場所で必要となる高価な計算をWITHクエリ内に配置して、冗長な作業を回避できます。別の可能なアプリケーションは、副作用のある関数の不要な複数の評価を防ぐことです。ただし、このコインの反対側は、オプティマイザーが通常のサブクエリよりも親クエリからWITHクエリに制限をプッシュすることができないことです。WITH
クエリは通常、親クエリが後で破棄する可能性のある行を抑制せずに、書き込まれたものとして評価されます。(ただし、前述のとおり上記では、クエリへの参照が限られた数の行のみを要求する場合、評価が早期に停止する可能性があります。)
大胆な強調鉱山。
SQLフィドル。 (Postgres 9.3の場合、それ以降のバージョンはまだ利用できません)
コードを修正してみてください
SELECT Acol1, Acol2, K.D.Bcol1, K.D.Bcol2, K.D.Ccol1, K.D.Ccol2
FROM tableA LEFT JOIN
( SELECT D.Bcol1, D.Bcol2, D.Ccol1, D.Ccol2
FROM (tableB FULL JOIN
tableC ON
Bcol1 = Ccol1 ) D ) K
ON Acol1 = K.D.Bcol1
K.D.を使用していますデータフローを示します。