Haruskah saya menggunakan SQL JOIN atau IN Clause?

13

Saya punya pertanyaan tentang pendekatan terbaik. Saya tidak yakin pendekatan mana yang terbaik ketika data dianggap variabel dalam ukuran.

Pertimbangkan 3 TABEL berikut:

KARYAWAN

EMPLOYEE_ID, EMP_NAME

PROYEK

PROJECT_ID, PROJ_NAME

EMP_PROJ (banyak ke banyak dari dua tabel di atas)

EMPLOYEE_ID, PROJECT_ID

Masalah : Diberikan EmployeeID, temukan SEMUA karyawan dari SEMUA Proyek yang dikaitkan dengan Karyawan ini.

Saya telah mencoba ini dalam dua cara .. kedua pendekatan hanya berbeda dengan beberapa milidetik tidak peduli ukuran data apa yang digunakan.

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)

Pergilah

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

Sampai sekarang, saya mengharapkan sekitar 5000 Karyawan dan Proyek masing-masing .. tetapi tidak tahu tentang apa yang ada banyak-banyak hubungan. Pendekatan mana yang akan Anda rekomendasikan? Terima kasih!

EDIT: Rencana Eksekusi Pendekatan 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)"

Rencana Pelaksanaan Pendekatan 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)"

Jadi sepertinya rencana Eksekusi Pendekatan 2 sedikit lebih baik, karena 'biaya' 60 dibandingkan dengan 85 pendekatan 1. Apakah itu cara yang tepat untuk menganalisis ini?

Bagaimana orang tahu itu akan berlaku bahkan untuk semua jenis banyak-banyak kombinasi?

rk2010
sumber
3
Sepertinya Postgres menjelaskan rencana kepada saya. Secara pribadi saya akan menggunakan pendekatan gabungan, tetapi membaca beberapa jawaban di bawah ini tentang menulis ulang kueri. Oh, dan saya sarankan menggunakan OP menjelaskan analisis daripada hanya menjelaskan.
xzilla
Saya setuju dengan xzilla: explain analyzemungkin mengungkapkan lebih banyak perbedaan antara paket
a_horse_with_no_name

Jawaban:

14

Dalam SQL Server, dengan beberapa asumsi seperti "bidang tersebut tidak dapat berisi NULLs", kueri tersebut harus memberikan rencana yang hampir sama.

Tetapi pertimbangkan juga tipe gabung yang Anda lakukan. Klausa IN seperti ini adalah Semi Gabung, bukan Gabung Dalam. Batin Gabung dapat memproyeksikan ke beberapa baris, sehingga memberikan duplikat (dibandingkan dengan menggunakan IN atau EXIS). Jadi, Anda mungkin ingin mempertimbangkan perilaku ini saat memilih cara Anda menulis kueri.

Rob Farley
sumber
2
Saya setuju dengan penggunaan yang ada daripada bergabung ketika mencoba untuk menghindari duplikasi. Dari pengalaman saya sendiri dengan SQL server ada dan bergabung dalam menghasilkan rencana permintaan yang sama pula. Saya memang memiliki beberapa kekhawatiran kinerja tentang pernyataan 'dalam' tetapi mereka hanya muncul ketika pilih dalam pernyataan di mulai mengembalikan beberapa ribu baris.
GrumpyMonkey
6
@ GrumpyMonkey - Dalam SQL Server 2005+ INdan EXISTSselalu memberikan rencana yang sama dalam pengalaman saya. NOT INdan NOT EXISTSberbeda namun dengan yang NOT EXISTSdisukai - Beberapa perbandingan kinerja di sini
Martin Smith
8

Apa yang dicari oleh kueri Anda adalah adil

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

atau

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
sumber
Bukankah sub-kueri akan lebih cepat jika SELECT 1bukan SELECT *?
Daniel Serodio
Mungkin tergantung pada DBMS. Saya tahu pasti bahwa SQL-Server mengoptimalkan Select *. (lih. Itzik Ben-Gan di Dasar-Dasar T-SQL Microsoft® SQL Server® 2012)
bernd_k
0

Anda dapat mencoba pertanyaan ini:


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
sumber