SQL JOIN veya IN yan tümcesi kullanmalı mıyım?


13

En iyi yaklaşım hakkında bir sorum var. Verilerin boyutu değişken olarak kabul edildiğinde hangi yaklaşımın en iyi olduğuna emin değilim.

Aşağıdaki 3 tabloyu göz önünde bulundurun:

ÇALIŞAN

EMPLOYEE_ID, EMP_NAME

PROJE

PROJECT_ID, PROJ_NAME

EMP_PROJ (yukarıdaki iki tablonun çoğundan çoğuna)

EMPLOYEE_ID, PROJECT_ID

Sorun : Bir Çalışan Kimliği verildiğinde, bu Çalışanın ilişkili olduğu TÜM Projelerin TÜM çalışanlarını bulun.

Bunu iki şekilde denedim. Hangi boyutta veri kullanılırsa kullanılsın her iki yaklaşım da yalnızca birkaç milisaniye kadar farklılık gösterir.

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)

Git

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

Şu an itibariyle her biri 5000 civarında çalışan ve proje bekliyorum. Hangi yaklaşımı önerirsiniz? Teşekkürler!

EDIT: Yaklaşım 1 Uygulama Planı

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

Yaklaşım 2 Uygulama Planı:

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

Yaklaşım 2'nin uygulama planı biraz daha iyi görünüyor, çünkü 'maliyet' yaklaşım 1'in 85'inin aksine 60'tır. Bunu analiz etmenin doğru yolu bu mu?

Bir çok kombinasyonun her türü için bile geçerli olacağını nasıl bilebilir?


3
Görünüşe göre bir Postgres bana planı açıklıyor. Şahsen ben katılma tabanlı yaklaşım ile gitmek istiyorum, ancak aşağıdaki sorguyu yeniden yazma ile ilgili bazı cevapları okuyun. Oh, ve ben OP açıklamak yerine açıklamak analiz kullanın öneririz.
xzilla

Xzilla'ya katılıyorum: explain analyzeplanlar arasında daha fazla farklılık ortaya çıkarabilir
a_horse_with_no_name

Yanıtlar:


14

SQL Server'da, "bu alanlar NULL içeremez" gibi birkaç varsayımla, bu sorgular hemen hemen aynı planı vermelidir.

Ancak yaptığınız birleştirme türünü de düşünün. Bunun gibi bir IN yan tümcesi, İç Birleştirme değil, Yarı Birleştirme'dir. İç Birleştirme birden çok satıra yansıtılabilir ve böylece yinelenmeler (IN veya EXISTS kullanımına kıyasla) verebilir. Bu nedenle, sorgunuzu nasıl yazacağınızı seçerken bu davranışı dikkate almak isteyebilirsiniz.


2
Çoğaltmadan yararlanmaya çalışırken bir birleşim yerine varolmanın kullanımını kabul ediyorum. SQL Server ile kendi deneyimimden var ve iç birleşim yine de aynı sorgu planını üretti. 'İn' ifadeleriyle ilgili bazı performans endişelerim vardı, ancak yalnızca in ifadesindeki seçim birkaç bin satır dönmeye başladığında ortaya çıktı.
GrumpyMonkey

6
@GrumpyMonkey - SQL Server 2005+ sürümünde INve EXISTSdeneyimlerime her zaman aynı planı verdim. NOT INve NOT EXISTSile ancak farklı NOT EXISTStercih - Bazı performans karşılaştırmaları burada
Martin Smith

8

Sorgunuzun aradığı şey

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

veya

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 1Bunun yerine alt sorgu daha hızlı olmaz mıydı SELECT *?
Daniel Serodio

DBMS'ye bağlı olabilir. SQL Server'ın Select * 'i optimize ettiğinden eminim. (bkz. Microsoft® SQL Server® 2012 T-SQL Fundamentals'ta Itzik Ben-Gan)
bernd_k

0

Bu sorguyu deneyebilirsiniz:


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;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.