Tôi nên sử dụng SQL THAM GIA hoặc IN khoản?


13

Tôi có một câu hỏi về cách tiếp cận tốt nhất. Tôi không chắc cách tiếp cận nào là tốt nhất khi dữ liệu được coi là có thể thay đổi kích thước.

Hãy xem xét 3 BẢNG sau:

NHÂN VIÊN

EMPLOYEE_ID, EMP_NAME

DỰ ÁN

PRO DỰ_ID, PROJ_NAME

EMP_PROJ (nhiều đến nhiều trong hai bảng trên)

EMPLOYEE_ID, PRO DỰ_ID

Vấn đề : Đưa ra một EmployeeID, tìm TẤT CẢ các nhân viên của TẤT CẢ các dự án mà Nhân viên này được liên kết.

Tôi đã thử điều này theo hai cách .. cả hai cách tiếp cận chỉ khác nhau vài mili giây cho dù kích thước của dữ liệu được sử dụng là bao nhiêu.

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)

đi

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

Cho đến bây giờ, tôi mong đợi khoảng 5000 nhân viên và dự án mỗi người .. nhưng không biết về mối quan hệ nhiều-nhiều loại tồn tại. Phương pháp nào bạn muốn giới thiệu? cảm ơn!

EDIT: Kế hoạch thực hiện phương pháp 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)"

Kế hoạch thực hiện phương pháp 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)"

Vì vậy, có vẻ như kế hoạch Thực hiện của Cách tiếp cận 2 tốt hơn một chút, vì 'chi phí' là 60 so với 85 của phương pháp 1. Đó có phải là cách đúng để phân tích điều này?

Làm thế nào để người ta biết nó sẽ đúng ngay cả đối với tất cả các loại kết hợp nhiều?


3
Có vẻ như một Postgres giải thích kế hoạch cho tôi. Cá nhân tôi sẽ đi theo cách tiếp cận dựa trên tham gia, nhưng đọc một số câu trả lời dưới đây về cách viết lại truy vấn. Ồ, và tôi đề nghị OP sử dụng phân tích giải thích thay vì chỉ giải thích.
xzilla

Tôi đồng ý với xzilla: explain analyzecó thể tiết lộ nhiều sự khác biệt giữa các kế hoạch
a_horse_with_no_name

Câu trả lời:


14

Trong SQL Server, với một vài giả định như "các trường đó không thể chứa NULL", các truy vấn đó sẽ đưa ra kế hoạch gần như giống nhau.

Nhưng cũng xem xét loại hình tham gia bạn đang làm. Mệnh đề IN như thế này là Semi Join, không phải Internal Join. Một kết nối bên trong có thể chiếu lên nhiều hàng, do đó tạo ra các bản sao (so với việc sử dụng IN hoặc EXISTS). Vì vậy, bạn có thể muốn xem xét hành vi này khi chọn cách bạn viết truy vấn của mình.


2
Tôi đồng ý với việc sử dụng tồn tại thay vì tham gia khi cố gắng tránh trùng lặp. Từ kinh nghiệm của riêng tôi với máy chủ SQL là sự tồn tại và tham gia bên trong đã tạo ra cùng một kế hoạch truy vấn. Tôi đã có một số lo ngại về hiệu suất về các câu lệnh 'in' nhưng chúng chỉ nổi lên khi phần chọn trong câu lệnh in bắt đầu trả về hàng nghìn hàng.
GrumpyMonkey

6
@GrumpyMonkey - Trong SQL Server 2005+ INEXISTSluôn đưa ra cùng một kế hoạch theo kinh nghiệm của tôi. NOT INNOT EXISTSkhác nhau tuy nhiên được NOT EXISTSưu tiên - Một số so sánh hiệu suất ở đây
Martin Smith

8

Những gì truy vấn của bạn đang tìm kiếm chỉ là

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

hoặc là

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

Không phải truy vấn phụ sẽ nhanh hơn nếu nó SELECT 1thay vì SELECT *?
Daniel Serodio

Có thể phụ thuộc vào DBMS. Tôi biết chắc chắn rằng SQL-Server đang tối ưu hóa Chọn *. (xem Itzik Ben-Gan trong Nguyên tắc cơ bản về T-SQL của Microsoft® SQL Server® 2012)
bernd_k

0

Bạn có thể thử truy vấn này:


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;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.