ฉันควรใช้ SQL เข้าร่วมหรือในข้อ?


13

ฉันมีคำถามเกี่ยวกับวิธีการที่ดีที่สุด ฉันไม่แน่ใจว่าวิธีใดดีที่สุดเมื่อข้อมูลถูกพิจารณาว่าเป็นขนาด

พิจารณา 3 ตารางต่อไปนี้:

พนักงาน

EMPLOYEE_ID, EMP_NAME

โครงการ

PROJECT_ID, PROJ_NAME

EMP_PROJ (มากไปหามากถึงสองตารางข้างต้น)

EMPLOYEE_ID, PROJECT_ID

ปัญหา : กำหนด EmployeeID ให้ค้นหาพนักงานทั้งหมดของโครงการทั้งหมดที่พนักงานคนนี้มีความเกี่ยวข้อง

ฉันได้ลองทำสองวิธี .. ทั้งสองวิธีต่างกันเพียงไม่กี่มิลลิวินาทีไม่ว่าจะใช้ข้อมูลขนาดใด

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)

ไป

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

ณ ตอนนี้ฉันคาดว่าจะมีพนักงานและโครงการประมาณ 5,000 คนต่อคน แต่ไม่มีความคิดเกี่ยวกับความสัมพันธ์ที่หลากหลาย คุณจะแนะนำวิธีใด ขอบคุณ!

แก้ไข: แผนการดำเนินการของแนวทาง 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)"

แผนการดำเนินการของแนวทาง 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)"

ดังนั้นดูเหมือนว่าแผนการดำเนินการของแนวทาง 2 จะดีกว่าเล็กน้อยเนื่องจาก 'ต้นทุน' เท่ากับ 60 เมื่อเทียบกับ 85 วิธี 1 นั่นเป็นวิธีที่ถูกต้องในการวิเคราะห์หรือไม่

เราจะรู้ได้อย่างไรว่ามันจะเป็นจริงแม้แต่กับชุดค่าผสมจำนวนมากทุกประเภท


3
ดูเหมือน Postgres อธิบายแผนให้ฉัน โดยส่วนตัวแล้วฉันจะใช้วิธีเข้าร่วม แต่อ่านคำตอบบางส่วนด้านล่างเกี่ยวกับการเขียนข้อความค้นหาซ้ำ โอ้และฉันขอแนะนำให้ใช้ OP อธิบายการวิเคราะห์มากกว่าเพียงอธิบาย
xzilla

ฉันเห็นด้วยกับ xzilla: explain analyzeอาจเปิดเผยความแตกต่างเพิ่มเติมระหว่างแผน
a_horse_with_no_name

คำตอบ:


14

ใน SQL Server ด้วยสมมติฐานบางอย่างเช่น "เขตข้อมูลเหล่านั้นไม่สามารถมีค่า NULL ได้" แบบสอบถามเหล่านั้นควรให้แผนเกือบเหมือนกัน

แต่ให้พิจารณาประเภทของการเข้าร่วมที่คุณทำ ประโยค IN แบบนี้เป็นการเข้าร่วมกึ่งไม่ใช่เข้าร่วมภายใน การเข้าร่วมภายในสามารถฉายลงบนหลาย ๆ แถวได้ดังนั้นจึงให้ข้อมูลซ้ำกัน (เปรียบเทียบกับการใช้ IN หรือ EXISTS) ดังนั้นคุณอาจต้องการพิจารณาพฤติกรรมนี้เมื่อเลือกวิธีที่คุณเขียนแบบสอบถามของคุณ


2
ฉันเห็นด้วยกับการใช้งานอยู่แทนที่จะเข้าร่วมเมื่อพยายามทำซ้ำ avaoid จากประสบการณ์ของฉันกับเซิร์ฟเวอร์ SQL คือการมีอยู่จริงและการรวมภายในทำให้เกิดแผนการสืบค้นเดียวกัน ฉันมีข้อกังวลเกี่ยวกับประสิทธิภาพบางประการเกี่ยวกับคำสั่ง 'in' แต่จะปรากฏเฉพาะเมื่อตัวเลือกในคำสั่ง in เริ่มกลับมาหลายพันแถว
GrumpyMonkey

6
@GrumpyMonkey - ใน SQL Server 2005+ INและEXISTSให้แผนเดียวกันในประสบการณ์ของฉัน NOT INและNOT EXISTSแตกต่างกันตาม แต่NOT EXISTSที่ต้องการ - การเปรียบเทียบประสิทธิภาพบางอย่างที่นี่
Martin Smith

8

ข้อความค้นหาของคุณกำลังมองหาอยู่

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

หรือ

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 1แทนSELECT *?
Daniel Serodio

อาจขึ้นอยู่กับ DBMS ฉันรู้แน่ว่า SQL-Server ปรับการเลือกไว้ * (cf. Itzik Ben-Gan ในMicrosoft® SQL Server® 2012 พื้นฐาน T-SQL)
bernd_k

0

คุณสามารถลองใช้แบบสอบถามนี้:


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;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.