2つの列の左外部結合のパフォーマンスの問題
質問
次の形式のようなSQLクエリを使用しています:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period
また、戻るには少なくとも4分かかるため、遅すぎるか、またはデッドロックが発生しています。これに変更する場合:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table1.period = table2.period
その後、正常に機能します(ただし、正しい列数を返しません)。これを高速化する方法はありますか?
UPDATE :後者のクエリの最後の2行を切り替えても同じことを行います:
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.period = table2.period
WHERE table1.person_uid = table2.person_uid
更新2:これらは実際に私が参加しているビューです。残念ながら、それらは私が制御できないデータベース上にあるため、インデックスを変更することは(簡単に)できません。ただし、これはインデックス作成の問題であることに同意したいと思います。このクエリを調整するための魔法の方法がわからない場合は、少し待ってから回答を受け入れます。それ以外の場合は、現在の回答のいずれかを受け入れ、自分がやりたいことを行う別の方法を見つけようとします。これまでの皆さんの助けに感謝します。
解決
ステートメント2と3は最初のステートメントとは異なることに注意してください。
どのように?さて、あなたは左外部結合を行っていますが、WHERE句はそれを考慮していません(ON句のように)。少なくとも、試してください:
SELECT col1, col2
FROM table1, table2
WHERE table1.person_uid = table2.person_uid (+)
AND table1.period = table2.period (+)
同じパフォーマンスの問題が発生するかどうかを確認します。
これらのテーブルにはどのようなインデックスがありますか?この関係は外部キー制約によって定義されていますか?
おそらく必要なのは、person_uidとperiod(両方のテーブル)の両方の複合インデックスです。
他のヒント
最後の2つが最初のクエリと同じではない理由を理解する必要があると思います。左結合を行い、結合の右側のテーブルのフィールドを参照するwhere句(最初のテーブルに一致するレコードが常にあるとは限らないフィールド)を追加すると、効果的に結合が変更されます。内部結合。これには1つの例外があり、それは次のようなものを参照する場合です
SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table2.person_uid is null
この場合、2番目のテーブルにレコードがないレコードを要求します。ただし、この特別な場合を除き、where句でtable2のフィールドを参照する場合、左結合を内部結合に変更します。
クエリの速度が十分でない場合は、インデックスを作成します。
提供された情報に基づいて誰かがあなたに言うことはすべて推測です。
クエリの実行プランを確認します。計画の遅延の理由がわからない場合は、ここに計画を投稿してください。
http://download.oracle .com / docs / cd / B28359_01 / server.111 / b28274 / ex_plan.htm#PFGRF009
両方のテーブルの person_uid
と period
のインデックスをカバーしていますか?
そうでない場合は、追加して再試行してください。
実行プランを見て、クエリが実際に実行していることを確認します。
また:フィールドのデータ型は何ですか?両方のテーブルで同じですか?暗黙のキャストは、本当に速度を低下させる可能性があります。
これらのテーブルには、結合する列にインデックスがありますか? Oracleの無料のSQLDeveloper製品をインストールし、それを使用して" explain"を実行します。そのクエリで、両方のテーブルの順次スキャンを実行しているかどうかを確認します。
左結合では、(person_uid、period)の一意の組み合わせごとにtable1をスキャンし、そこで対応するすべてのレコードをtable2で検索します。 table2に適切なインデックスがない場合、これにはそのテーブル全体のスキャンも含まれます。
実行計画が表示されない場合の一番の推測は、最初のクエリ(正しいと思われる唯一のクエリ)がtable1と同様にtable2もスキャンすることです。
インデックスを変更できないと言っているように、クエリを変更する必要があります。私が知る限り、現実的な選択肢は1つだけです...
SELECT
col1, col2
FROM
table2
FULL OUTER JOIN
table1
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period
WHERE
table1.person_uid IS NOT NULL
ここでの希望は、table2を(person_uid、period)の一意の組み合わせごとにスキャンし、table1のインデックスを使用することです。 (table1をスキャンし、table2のインデックスを使用するのとは対照的に、これはクエリから予想したものです。)
ただし、table1に適切なインデックスがない場合、パフォーマンスの改善はまったく見られません...
権限。
更新の1つで、OPは、テーブルではなくビューを実際にクエリしていると述べています。この場合、特にビューが複雑で、必要な情報を含まない他の多くのテーブルまたはビューを呼び出すビューである場合は、必要なテーブルを直接クエリすることでパフォーマンスを向上させることができます。
ANSI結合構文は、JOIN条件とFILTER述語を非常に明確に区別します。これは、外部結合を作成するときに非常に重要です。 emp / deptテーブルを使用して、次の2つの外部結合の結果を確認します
Q1
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on d.deptno = e.deptno
and loc in ('NEW YORK','BOSTON' )
;
DNAME DEPTNO ENAME MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING 10 CLARK 7839 NEW YORK
ACCOUNTING 10 KING NEW YORK
ACCOUNTING 10 MILLER 7782 NEW YORK
RESEARCH 20 DALLAS
SALES 30 CHICAGO
OPERATIONS 40 BOSTON
====
Q2
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on d.deptno = e.deptno
where loc in ('NEW YORK','BOSTON' )
;
DNAME DEPTNO ENAME MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING 10 CLARK 7839 NEW YORK
ACCOUNTING 10 KING NEW YORK
ACCOUNTING 10 MILLER 7782 NEW YORK
OPERATIONS 40 BOSTON
Q1が示す最初の例は、「定数に結合する」例です。基本的に、外部結合を実行する前にフィルター条件が適用されます。したがって、外部結合の一部として後で追加される行を削除します。必ずしも間違っているわけではありませんが、それはあなたが本当に求めたクエリですか?多くの場合、(外部)結合の後にフィルターが適用されるQ2に示される結果が必要です。
大規模なデータセットの場合も、パフォーマンスへの影響があります。多くの場合、定数への結合は、横方向のビューを作成することにより、オプティマイザーによって内部的に解決する必要があります。これは通常、ハッシュ結合ではなくネストされたループ結合によってのみ最適化できます
Oracleの外部結合構文に精通している開発者にとって、クエリはおそらく次のように記述されているはずです
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
,emp e
where d.deptno = e.deptno(+)
and loc in ('NEW YORK','BOSTON' )
このクエリは、上記のQ2と意味的に同等です。
要約すると、ANSI外部結合を記述するときは、JOIN句とWHERE句の違いを理解することが非常に重要です。