Thực hành tốt nhất giữa việc sử dụng LEFT THAM GIA hoặc KHÔNG EXIST


67

Có cách thực hành tốt nhất giữa việc sử dụng định dạng TRÁI PHIẾU hoặc định dạng KHÔNG EXISTS không?

Lợi ích của việc sử dụng cái này hơn cái kia là gì?

Nếu không, nên được ưu tiên?

SELECT *
FROM tableA A
LEFT JOIN tableB B
     ON A.idx = B.idx
WHERE B.idx IS NULL

SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)

Tôi đang sử dụng các truy vấn trong Access đối với cơ sở dữ liệu SQL Server.


2
Là một sang một bên, cách tiếp cận dường như giống hệt nhau WHERE A.idx NOT IN (...) không giống nhau do các hành vi trivalent của NULL(tức NULLlà không bằng NULL(và cũng không bất bình đẳng), do đó nếu bạn có bất kỳ NULL trong tableBbạn sẽ nhận được kết quả bất ngờ!)
Elaskanator

Câu trả lời:


58

Sự khác biệt lớn nhất không nằm ở sự tham gia so với không tồn tại, đó là (như được viết) , SELECT *.

Trong ví dụ đầu tiên, bạn nhận được tất cả các cột từ cả hai ABtrong khi trong ví dụ thứ hai, bạn chỉ nhận được các cột từ đó A.

Trong SQL Server, biến thể thứ hai nhanh hơn một chút trong một ví dụ giả định rất đơn giản:

Tạo hai bảng mẫu:

CREATE TABLE dbo.A
(
    A_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);

CREATE TABLE dbo.B
(
    B_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);
GO

Chèn 10.000 hàng vào mỗi bảng:

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

Xóa mọi hàng thứ 5 khỏi bảng thứ hai:

DELETE 
FROM dbo.B 
WHERE B_ID % 5 = 1;

SELECT COUNT(*) -- shows 10,000
FROM dbo.A;

SELECT COUNT(*) -- shows  8,000
FROM dbo.B;

Thực hiện hai SELECTbiến thể tuyên bố thử nghiệm :

SELECT *
FROM dbo.A
    LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;

SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
    FROM dbo.B
    WHERE b.B_ID = a.A_ID);

Kế hoạch thực hiện:

nhập mô tả hình ảnh ở đây

Biến thể thứ hai không cần thực hiện thao tác lọc vì nó có thể sử dụng toán tử chống bán nối trái.


23

Về mặt logic, chúng giống hệt nhau, nhưng NOT EXISTSgần với AntiSemiJoin mà bạn yêu cầu và thường được ưa thích hơn. Nó cũng nhấn mạnh tốt hơn rằng bạn không thể truy cập các cột trong B, vì nó chỉ được sử dụng làm bộ lọc (trái ngược với việc chúng có sẵn với các giá trị NULL).

Nhiều năm trước (SQL Server 6.0 ish), LEFT JOINđã nhanh hơn, nhưng điều đó đã không xảy ra trong một thời gian rất dài. Những ngày này, NOT EXISTSlà nhanh hơn một chút.


Tác động lớn nhất trong Access là JOINphương thức phải hoàn thành phép nối trước khi lọc nó, xây dựng tập đã nối trong bộ nhớ. Sử dụng NOT EXISTSnó kiểm tra hàng nhưng không phân bổ không gian cho các cột. Thêm vào đó, nó dừng tìm kiếm một khi nó tìm thấy một hàng. Hiệu suất thay đổi nhiều hơn một chút trong Access, nhưng một nguyên tắc chung là NOT EXISTScó xu hướng nhanh hơn một chút. Tôi sẽ ít có khuynh hướng nói đó là "thực hành tốt nhất", vì có nhiều yếu tố liên quan hơn.


6

Một ngoại lệ mà tôi nhận thấy NOT EXISTSlà vượt trội (tuy nhiên không đáng kể) LEFT JOIN ... WHERE IS NULLlà khi sử dụng Máy chủ được liên kết .

Từ việc kiểm tra các kế hoạch thực hiện, có vẻ như NOT EXISTStoán tử được thực thi theo kiểu vòng lặp lồng nhau. Do đó, nó được thực hiện trên cơ sở mỗi hàng (mà tôi cho là có ý nghĩa).

Ví dụ kế hoạch thực hiện thể hiện hành vi này: nhập mô tả hình ảnh ở đây


1
Các máy chủ được liên kết là tàn bạo cho loại điều đó. Một cách tiếp cận khả thi để giải quyết vấn đề đó là sao chép dữ liệu từ xa qua liên kết máy chủ được liên kết bằng cách sử dụng đơn giản INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=ysau đó chạy NOT EXISTS (...)mệnh đề đối với bản sao tạm thời của cơ sở dữ liệu.
Max Vernon

2
Một chút rụt rè ngay bây giờ để nhận được phản hồi từ Max Vernon trên bài đăng của tôi! Fanboy đang bỏ qua một bên. Thật buồn cười khi bạn đề cập đến điều đó, vì tôi đã sử dụng cách tiếp cận chính xác đó trong một số trường hợp để tận dụng tối đa các tình huống máy chủ chéo đó.
robopim

1
Chúc mừng, @pimbrouwers - cảm ơn vì nhận xét của bạn!
Max Vernon

5

Nói chung, công cụ sẽ tạo ra một kế hoạch thực hiện dựa trên:

  1. Số lượng hàng trong A và B
  2. Cho dù có Chỉ số trên A và / hoặc B.
  3. Số lượng hàng kết quả dự kiến ​​(và hàng trung gian)
  4. Hình thức của truy vấn đầu vào (tức là câu hỏi của bạn)

Dành cho (4):

Kế hoạch "không tồn tại" khuyến khích kế hoạch tìm kiếm dựa trên bảng B. Đây là một lựa chọn tốt khi bảng A nhỏ và bảng B lớn (và chỉ số tồn tại trên B).

Kế hoạch "antijoin" là một lựa chọn tốt khi bảng A rất lớn hoặc bảng B rất nhỏ hoặc không có chỉ số trên B và trả về một tập kết quả lớn.

Tuy nhiên, đó chỉ là một "sự khích lệ", giống như một đầu vào có trọng số. Một mạnh mẽ (1), (2), (3) thường đưa ra lựa chọn cho (4) moot.

(Bỏ qua ảnh hưởng của ví dụ của bạn trả về các cột khác nhau do dấu *, được giải quyết bằng câu trả lời @MaxVernon.).

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.