Czy powinienem używać SQL JOIN lub IN Clause?

13

Mam pytanie dotyczące najlepszego podejścia. Nie jestem pewien, które podejście jest najlepsze, gdy dane są uważane za zmienne pod względem wielkości.

Rozważ następujące 3 TABELE:

PRACOWNIK

EMPLOYEE_ID, EMP_NAME

PROJEKT

PROJECT_ID, PROJ_NAME

EMP_PROJ (wiele do wielu z powyższych dwóch tabel)

EMPLOYEE_ID, PROJECT_ID

Problem : Biorąc pod uwagę identyfikator pracownika, znajdź WSZYSTKICH pracowników WSZYSTKICH projektów, z którymi ten pracownik jest powiązany.

Próbowałem tego na dwa sposoby ... oba podejścia różnią się tylko o kilka milisekund, bez względu na to, jaki rozmiar danych jest używany.

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)

udać się

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

Na dzień dzisiejszy oczekuję około 5000 pracowników i projektów .. ale nie mam pojęcia o tym, jaki rodzaj relacji istnieje wiele osób. Które podejście poleciłbyś? dzięki!

EDYCJA: Plan realizacji podejścia 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)"

Plan realizacji podejścia 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)"

Wygląda więc na to, że plan wykonania Podejścia 2 jest nieco lepszy, ponieważ „koszt” wynosi 60, a nie 85 podejścia 1. Czy to właściwy sposób na analizę tego?

Skąd wiadomo, że będzie to prawdą nawet w przypadku wielu kombinacji wielu kombinacji?

rk2010
źródło
3
Wygląda mi na plan wyjaśniający Postgresa. Osobiście wybrałbym podejście oparte na łączeniu, ale przeczytałem niektóre z poniższych odpowiedzi na temat przepisywania zapytania. Aha, i zasugerowałbym użycie OP wyjaśnienia analizy zamiast po prostu wyjaśnienia.
xzilla
Zgadzam się z Xzillą: explain analyzemoże ujawnić więcej różnic między planami
a_horse_with_no_name

Odpowiedzi:

14

W SQL Server, z kilkoma założeniami, takimi jak „te pola nie mogą zawierać wartości NULL”, zapytania te powinny dawać prawie ten sam plan.

Ale również weź pod uwagę rodzaj wykonywanego łączenia. Taka klauzula IN to Semi Join, a nie Internal Join. Sprzężenie wewnętrzne może rzutować na wiele wierszy, dając w ten sposób duplikaty (w porównaniu do użycia IN lub EXISTS). Możesz więc rozważyć to zachowanie przy wyborze sposobu pisania zapytania.

Rob Farley
źródło
2
Zgadzam się z użyciem istnienia zamiast łączenia, kiedy próbuję uniknąć powielania. Z mojego własnego doświadczenia z serwerem SQL wynika, że ​​połączenie wewnętrzne i tak stworzyło ten sam plan zapytań. Miałem pewne obawy dotyczące wydajności instrukcji „in”, ale pojawiły się one dopiero wtedy, gdy instrukcja select in in zaczęła zwracać kilka tysięcy wierszy.
GrumpyMonkey
6
@GrumpyMonkey - W SQL Server 2005+ INi EXISTSzawsze podaj ten sam plan z mojego doświadczenia. NOT INi NOT EXISTSróżnią się jednak NOT EXISTSpreferowanymi - Tutaj są niektóre porównania wydajności
Martin Smith,
8

To, czego szuka twoje zapytanie, jest właśnie

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

lub

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
źródło
Czy pod-zapytanie nie byłoby szybsze, gdyby było SELECT 1zamiast SELECT *?
Daniel Serodio
Może zależeć od DBMS. Wiem na pewno, że SQL Server optymalizuje Select *. (por. Itzik Ben-Gan w Microsoft® SQL Server® 2012 T-SQL Podstawy)
bernd_k
0

Możesz spróbować tego zapytania:


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
źródło