Devo usare la clausola SQL JOIN o IN?


13

Ho una domanda sul miglior approccio. Non sono sicuro quale sia l'approccio migliore quando i dati sono considerati di dimensioni variabili.

Considera i seguenti 3 TABELLE:

DIPENDENTE

EMPLOYEE_ID, EMP_NAME

PROGETTO

PROJECT_ID, PROJ_NAME

EMP_PROJ (da molte a molte delle due tabelle precedenti)

EMPLOYEE_ID, PROJECT_ID

Problema : dato un EmployeeID, trova TUTTI i dipendenti di TUTTI i progetti a cui questo dipendente è associato.

L'ho provato in due modi ... entrambi gli approcci differiscono solo di pochi millisecondi, indipendentemente dalla dimensione dei dati utilizzati.

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)

partire

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

Per ora, mi aspetto circa 5000 dipendenti e progetti ciascuno .. ma non ho idea di che tipo di relazione esiste. Quale approccio consiglieresti? Grazie!

EDIT: piano di esecuzione dell'approccio 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)"

Piano di esecuzione dell'approccio 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)"

Sembra quindi che il piano di esecuzione dell'approccio 2 sia leggermente migliore, perché il "costo" è 60 rispetto a 85 dell'approccio 1. È il modo giusto di analizzarlo?

Come si fa a sapere che sarà vero anche per tutti i tipi di molte-molte combinazioni?


3
Sembra che Postgres mi spieghi il piano. Personalmente andrei con l'approccio basato su join, ma leggo alcune delle risposte seguenti sulla riscrittura della query. Oh, e suggerirei che l'uso del PO spieghi analizza piuttosto che spiegare.
xzilla,

Sono d'accordo con xzilla: explain analyzepotrebbe rivelare più differenze tra i piani
a_horse_with_no_name

Risposte:


14

In SQL Server, con alcune ipotesi come "quei campi non possono contenere NULL", queste query dovrebbero fornire quasi lo stesso piano.

Ma considera anche il tipo di join che stai facendo. Una clausola IN come questa è un Semi Join, non un Inner Join. Un Join interno può proiettare su più righe, dando così duplicati (rispetto all'utilizzo di IN o EXISTS). Quindi potresti voler considerare questo comportamento quando scegli come scrivere la tua query.


2
Sono d'accordo con l'uso di esiste piuttosto che un join quando si tenta di evitare la duplicazione. Dalla mia esperienza con SQL Server esisteva il join interno e lo stesso piano di query prodotto comunque. Avevo alcuni problemi di prestazioni riguardo alle istruzioni "in", ma sono emerse solo quando la selezione nell'istruzione in ha iniziato a restituire diverse migliaia di righe.
GrumpyMonkey,

6
@GrumpyMonkey - In SQL Server 2005+ INe fornisci EXISTSsempre lo stesso piano nella mia esperienza. NOT INe NOT EXISTSsono comunque diversi tra quelli NOT EXISTSpreferiti - Alcuni confronti di prestazioni qui
Martin Smith,

8

Ciò che la tua ricerca sta cercando è giusto

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

o

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 );

La sottoquery non sarebbe più veloce se fosse SELECT 1invece di SELECT *?
Daniel Serodio,

Potrebbe dipendere da DBMS. So per certo che SQL Server sta ottimizzando Select *. (cfr. Itzik Ben-Gan in Microsoft® SQL Server® 2012 T-SQL Fundamentals)
bernd_k,

0

Puoi provare questa query:


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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.