web-dev-qa-db-ja.com

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

PDATE 2:これらは実際に参加しているビューです。残念ながら、それらは私が制御できないデータベース上にあるため、(簡単に)インデックスに変更を加えることはできません。私はこれが索引付けの問題であることに同意する傾向があります。このクエリを調整する魔法の方法がわからない場合に備えて、回答を受け入れるまで少しお待ちください。それ以外の場合は、現在の回答の1つを受け入れて、自分がやりたいことを行う別の方法を見つけようとします。これまで皆さんの助けに感謝します。

11
Jason Baker

ステートメント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の両方(両方のテーブル)の複合インデックスです。

16
cletus

最後の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のフィールドを参照すると、左結合が内部結合に変更されます。

クエリの速度が十分でない場合は、インデックスを調べます。

5
HLGEM

あなたが提供した情報に基づいて誰かがあなたに言うことはすべて推測です。

クエリの実行プランを確認します。計画が遅い理由が見当たらない場合は、ここに計画を投稿してください。

http://download.Oracle.com/docs/cd/B28359_01/server.111/b28274/ex_plan.htm#PFGRF009

4
Dave Costa

両方のテーブルのperson_uidperiodのインデックスをカバーしていますか?

そうでない場合は、それらを追加して再試行してください。

実行プランを見て、クエリが実際に何をしているかを確認します。

また、フィールドのデータ型は何ですか?両方のテーブルで同じですか?暗黙のキャストは、物事を本当に遅くする可能性があります。

3
Andrew Rollings

左側の結合では、(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

ここでの希望は、(person_uid、period)の一意の組み合わせごとにtable2をスキャンし、table1のインデックスを利用することです。 (table1をスキャンしてtable2のインデックスを利用するのとは対照的に、これはクエリで期待したとおりです。)

ただし、table1に適切なインデックスがない場合は、パフォーマンスが向上する可能性はほとんどありません...

Dems。

2
MatBailie

これらのテーブルには、結合する列のインデックスがありますか? Oracleの無料のSQLDeveloper製品をインストールし、それを使用してそのクエリに対して「説明」を行い、両方のテーブルの順次スキャンを実行しているかどうかを確認します。

2
Paul Tomblin

更新の1つで、OPはテーブルではなくビューを実際にクエリしていると述べています。この場合、特にビューが複雑で、必要な情報が含まれていない、またはビューを呼び出すビューである他の多くのテーブルに結合したりする場合に、必要なテーブルに直接クエリを実行すると、パフォーマンスが向上する可能性があります。

0
HLGEM

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句の違いを理解することが非常に重要です。

0
BobC