SQL JOINまたはIN句を使用する必要がありますか?


13

最善のアプローチについて質問があります。データのサイズが可変であると考えられる場合、どのアプローチが最適かはわかりません。

次の3つの表を検討してください。

社員

EMPLOYEE_ID、EMP_NAME

事業

PROJECT_ID、PROJ_NAME

EMP_PROJ(上記の2つのテーブルの多くに多数)

EMPLOYEE_ID、PROJECT_ID

問題:EmployeeIDを指定すると、このEmployeeが関連付けられているすべてのプロジェクトのすべての従業員を見つけます。

私はこれを2つの方法で試しました。どちらのアプローチも、使用するデータのサイズに関係なく、数ミリ秒だけ異なります。

SELECT EMP_NAME FROM EMPLOYEE
WHERE EMPLOYEE_ID IN (
    SELECT EMPLOYEE_ID FROM EMP_PROJ    
    WHERE PROJECT_ID IN (
        SELECT PROJECT_ID FROM EMP_PROJ p, EMPLOYEE e
        WHERE p.EMPLOYEE_ID = E.EMPLOYEE_ID 
        AND  E.EMPLOYEE_ID = 123)

行く

select c.EMP_NAME FROM
(SELECT PROJECT_ID FROM EMP_PROJ
WHERE EMPLOYEE_ID = 123) a
JOIN 
EMP_PROJ b
ON a.PROJECT_ID = b.PROJECT_ID
JOIN 
EMPLOYEE c
ON b.EMPLOYEE_ID = c.EMPLOYEE_ID

今のところ、私はそれぞれ約5000人の従業員とプロジェクトを期待しています。どのアプローチをお勧めしますか?ありがとう!

編集:アプローチ1の実行計画

"Hash Join  (cost=86.55..106.11 rows=200 width=98)"
"  Hash Cond: (employee.employee_id = emp_proj.employee_id)"
"  ->  Seq Scan on employee  (cost=0.00..16.10 rows=610 width=102)"
"  ->  Hash  (cost=85.07..85.07 rows=118 width=4)"
"        ->  HashAggregate  (cost=83.89..85.07 rows=118 width=4)"
"              ->  Hash Semi Join  (cost=45.27..83.60 rows=118 width=4)"
"                    Hash Cond: (emp_proj.project_id = p.project_id)"
"                    ->  Seq Scan on emp_proj  (cost=0.00..31.40 rows=2140 width=8)"
"                    ->  Hash  (cost=45.13..45.13 rows=11 width=4)"
"                          ->  Nested Loop  (cost=0.00..45.13 rows=11 width=4)"
"                                ->  Index Scan using employee_pkey on employee e  (cost=0.00..8.27 rows=1 width=4)"
"                                      Index Cond: (employee_id = 123)"
"                                ->  Seq Scan on emp_proj p  (cost=0.00..36.75 rows=11 width=8)"
"                                      Filter: (p.employee_id = 123)"

アプローチ2の実行計画:

"Nested Loop  (cost=60.61..112.29 rows=118 width=98)"
"  ->  Index Scan using employee_pkey on employee e  (cost=0.00..8.27 rows=1 width=4)"
"        Index Cond: (employee_id = 123)"
"  ->  Hash Join  (cost=60.61..102.84 rows=118 width=102)"
"        Hash Cond: (b.employee_id = c.employee_id)"
"        ->  Hash Join  (cost=36.89..77.49 rows=118 width=8)"
"              Hash Cond: (b.project_id = p.project_id)"
"              ->  Seq Scan on emp_proj b  (cost=0.00..31.40 rows=2140 width=8)"
"              ->  Hash  (cost=36.75..36.75 rows=11 width=8)"
"                    ->  Seq Scan on emp_proj p  (cost=0.00..36.75 rows=11 width=8)"
"                          Filter: (employee_id = 123)"
"        ->  Hash  (cost=16.10..16.10 rows=610 width=102)"
"              ->  Seq Scan on employee c  (cost=0.00..16.10 rows=610 width=102)"

「コスト」はアプローチ1の85に対して60であるため、アプローチ2の実行計画はわずかに優れているように見えます。これを分析する正しい方法はありますか?

あらゆる種類の多対多の組み合わせに対しても、それが当てはまることをどのように知るのでしょうか?


3
Postgresが私に計画を説明しているように見えます。個人的には、結合ベースのアプローチを使用しますが、クエリの書き換えに関する以下の回答の一部をお読みください。ああ、私はOPの使用が単に説明するのではなく、分析を説明することをお勧めします。
xzilla

私はxzillaに同意します:explain analyze計画間のより多くの違いを明らかにするかもしれません
a_horse_with_no_name

回答:


14

SQL Serverでは、「これらのフィールドにNULLを含めることはできません」などのいくつかの前提条件で、これらのクエリはほぼ同じプランを提供する必要があります。

ただし、実行している結合のタイプも考慮してください。このようなIN句は、内部結合ではなく、半結合です。内部結合は複数の行に投影できるため、重複が発生します(INまたはEXISTSを使用する場合と比較して)。そのため、クエリの記述方法を選択するときに、この動作を検討することをお勧めします。


2
Avaoidの複製を試みる場合、結合ではなく存在の使用に同意します。SQL Serverでの私自身の経験から、とにかく同じクエリプランが生成され、内部結合が存在しました。「in」ステートメントに関するパフォーマンスの懸念がいくつかありましたが、inステートメントのselectが数千行を返し始めたときにのみ表面化しました。
-GrumpyMonkey

6
@GrumpyMonkey-SQL Server 2005+ではINEXISTS常に私の経験で同じ計画を立てます。NOT INそして、NOT EXISTSしてしかし、異なるNOT EXISTS優先- ここではいくつかのパフォーマンス比較
マーティン・スミス

8

クエリが探しているのは

SELECT EMP_NAME 
FROM EMPLOYEE e
WHERE E.EMPLOYEE_ID = 123
and exists (select * from EMP_PROJ  where  EMPLOYEE_ID = 123);

または

SELECT EMP_NAME 
FROM EMPLOYEE e
WHERE E.EMPLOYEE_ID = 123
and exists (select * from EMP_PROJ ep where  ep.EMPLOYEE_ID = E.EMPLOYEE_ID );

SELECT 1代わりにあった場合、サブクエリは高速になりませんSELECT *か?
ダニエルセロディオ

DBMSに依存する場合があります。SQL ServerがSelect *を最適化していることは確かです。(Microsoft®SQLServer®2012 T-SQLの基礎のItzik Ben-Ganを参照)
bernd_k

0

このクエリを試すことができます:


select distinct e2.employee_id, ep.project_id 
from employee e, employee e2, emp_proj ep
where
e.employee_id = 123
and e.employee_id = ep.employee_id
and e2.project_id = ep.project_id;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.