Soll ich SQL JOIN oder IN-Klausel verwenden?

13

Ich habe eine Frage zum besten Ansatz. Ich bin nicht sicher, welcher Ansatz am besten ist, wenn Daten als variabel in der Größe angesehen werden.

Betrachten Sie die folgenden 3 Tabellen:

MITARBEITER

EMPLOYEE_ID, EMP_NAME

PROJEKT

PROJECT_ID, PROJ_NAME

EMP_PROJ (viele bis viele der obigen zwei Tabellen)

EMPLOYEE_ID, PROJECT_ID

Problem : Suchen Sie mit einer bestimmten Mitarbeiter-ID nach ALLEN Mitarbeitern ALLER Projekte, denen dieser Mitarbeiter zugeordnet ist.

Ich habe dies auf zwei Arten versucht. Beide Ansätze unterscheiden sich nur um wenige Millisekunden, unabhängig von der Größe der verwendeten Daten.

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)

gehen

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

Ab sofort erwarte ich ungefähr 5000 Mitarbeiter und Projekte pro Person. Ich habe jedoch keine Ahnung, in welcher Beziehung sich viele Menschen befinden. Welchen Ansatz würden Sie empfehlen? Vielen Dank!

BEARBEITEN: Ausführungsplan von Ansatz 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)"

Ausführungsplan von Ansatz 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)"

Sieht also so aus, als wäre der Ausführungsplan von Ansatz 2 etwas besser, da die „Kosten“ bei Ansatz 1 bei 60 gegenüber 85 liegen. Ist das der richtige Weg, dies zu analysieren?

Woher weiß man, dass es auch für viele verschiedene Kombinationen gilt?

rk2010
quelle
3
Sieht aus wie ein Postgres mir Plan erklären. Persönlich würde ich mich für den Join-basierten Ansatz entscheiden, aber einige der unten stehenden Antworten zum Umschreiben der Abfrage lesen. Oh, und ich würde vorschlagen, dass das OP EXPLAIN ANALYSE verwendet und nicht nur erklärt.
Xzilla
Ich stimme xzilla zu: explain analyzeMöglicherweise gibt es weitere Unterschiede zwischen den Plänen
a_horse_with_no_name

Antworten:

14

In SQL Server sollten diese Abfragen mit einigen Annahmen wie "Diese Felder dürfen keine NULL-Werte enthalten" fast denselben Plan enthalten.

Berücksichtigen Sie aber auch die Art des Joins, den Sie ausführen. Eine IN-Klausel wie diese ist ein Semi-Join, kein Inner-Join. Ein Inner Join kann auf mehrere Zeilen projiziert werden, wodurch Duplikate entstehen (im Vergleich zur Verwendung von IN oder EXISTS). Daher sollten Sie dieses Verhalten berücksichtigen, wenn Sie festlegen, wie Sie Ihre Abfrage schreiben.

Rob Farley
quelle
2
Ich bin damit einverstanden, dass bei der Vermeidung von Duplikaten statt eines Joins die Verwendung von vorhanden ist. Aus eigener Erfahrung mit SQL Server wurde der vorhandene und der innere Join ohnehin den gleichen Abfrageplan erzeugt. Ich hatte einige Performance-Bedenken bezüglich In-Anweisungen, aber diese tauchten erst auf, als die Auswahl in der In-Anweisung begann, mehrere tausend Zeilen zurückzugeben.
GrumpyMonkey
6
@GrumpyMonkey - In SQL Server 2005+ INund EXISTSnach meiner Erfahrung immer den gleichen Plan angeben . NOT INund NOT EXISTSsind jedoch mit NOT EXISTSVorzug anders - Einige Leistungsvergleiche hier
Martin Smith
8

Was Ihre Anfrage sucht, ist einfach

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

oder

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 );
bernd_k
quelle
Wäre das nicht die Unterabfrage schneller , wenn es SELECT 1statt SELECT *?
Daniel Serodio
Könnte von DBMS abhängen. Ich weiß mit Sicherheit, dass SQL-Server Select * optimiert. (vgl. Itzik Ben-Gan in Microsoft® SQL Server® 2012 T-SQL-Grundlagen)
bernd_k
0

Sie können diese Abfrage versuchen:


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;
techExplorer
quelle