Cách tốt nhất để viết Truy vấn SQL kiểm tra một cột cho giá trị không phải NULL hoặc NULL


17

Tôi có một SP với tham số có NULL làm giá trị mặc định và sau đó tôi muốn thực hiện một truy vấn như thế này:

SELECT ...
FROM ...
WHERE a.Blah = @Blah AND (a.VersionId = @VersionId OR (@VersionId IS NULL AND a.VersionId IS NULL));

Các WHEREkiểm tra trên cho cả giá trị không phải NULL và giá trị NULL cho @VersionId.

Thay vào đó, sẽ tốt hơn nếu sử dụng một IFcâu lệnh và sao chép truy vấn thành một truy vấn tìm kiếm không phải NULL và một câu hỏi khác cho NULL như vậy? :

IF @VersionId IS NULL BEGIN
    SELECT ...
    FROM ...
    WHERE a.Blah = @Blah AND a.VersionId IS NULL;
ELSE BEGIN
    SELECT ...
    FROM ...
    WHERE a.Blah = @Blah AND a.VersionId = @VersionId;
END

Hoặc trình tối ưu hóa truy vấn làm cho nó về cơ bản giống nhau?

CẬP NHẬT:

(Lưu ý: Tôi đang sử dụng SQL Server)

(Và theo như tôi biết, sử dụng a.VersionId = @VersionIdcho cả hai trường hợp sẽ không hoạt động, phải không?)



Tôi thường sử dụng như sau: ISNULL (a.VersionId, @VersionId) = @VersionId
628426

Câu trả lời:


36

Mẫu này

column = @argument OR (@argument IS NULL AND column IS NULL)

có thể được thay thế bằng

EXISTS (SELECT column INTERSECT SELECT @argument)

Điều này sẽ cho phép bạn khớp NULL với NULL và sẽ cho phép động cơ sử dụng một chỉ mục một columncách hiệu quả. Để phân tích sâu về kỹ thuật này, tôi giới thiệu bạn đến bài viết trên blog của Paul White:

Vì có hai đối số trong trường hợp cụ thể của bạn, bạn có thể sử dụng cùng một kỹ thuật đối sánh với @Blah- theo cách đó bạn sẽ có thể viết lại toàn bộ mệnh đề WHERE ít nhiều chính xác:

WHERE
  EXISTS (SELECT a.Blah, a.VersionId INTERSECT SELECT @Blah, @VersionId)

Điều này sẽ làm việc nhanh chóng với một chỉ số trên (a.Blah, a.VersionId).


Hoặc trình tối ưu hóa truy vấn làm cho nó về cơ bản giống nhau?

Trong trường hợp này, có. Trong tất cả các phiên bản (ít nhất) từ SQL Server 2005 trở đi, trình tối ưu hóa có thể nhận ra mẫu col = @var OR (@var IS NULL AND col IS NULL)và thay thế bằng mẫu ISso sánh thích hợp . Điều này không phụ thuộc vào kết hợp viết lại nội bộ, vì vậy có thể có những trường hợp phức tạp hơn trong đó điều này không phải lúc nào cũng đáng tin cậy.

Trong các phiên bản SQL Server từ 2008 SP1 CU5 đã bao gồm , bạn cũng có tùy chọn sử dụng Tối ưu hóa nhúng tham số thông qua OPTION (RECOMPILE), trong đó giá trị thời gian chạy của bất kỳ tham số hoặc biến nào được nhúng trong truy vấn dưới dạng chữ trước khi biên dịch.

Vì vậy, ít nhất là ở một mức độ lớn, trong trường hợp này, sự lựa chọn là một vấn đề về phong cách, mặc dù việc INTERSECTxây dựng là không thể phủ nhận nhỏ gọn và thanh lịch.

Các ví dụ sau đây cho thấy kế hoạch thực hiện 'giống nhau' cho mỗi biến thể (loại trừ các tham chiếu biến đổi được loại trừ):

DECLARE @T AS table
(
    c1 integer NULL,
    c2 integer NULL,
    c3 integer NULL

    UNIQUE CLUSTERED (c1, c2)
);

-- Some data
INSERT @T
    (c1, c2, c3)
SELECT 1, 1, 1 UNION ALL
SELECT 2, 2, 2 UNION ALL
SELECT NULL, NULL, NULL UNION ALL
SELECT 3, 3, 3;

-- Filtering conditions
DECLARE 
    @c1 integer,
    @c2 integer;

SELECT
    @c1 = NULL,
    @c2 = NULL;

-- Writing the NULL-handling out explicitly
SELECT * 
FROM @T AS T
WHERE 
(
    T.c1 = @c1
    OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND 
(
    T.c2 = @c2
    OR (@c2 IS NULL AND T.c2 IS NULL)
);

-- Using INTERSECT
SELECT * 
FROM @T AS T
WHERE EXISTS 
(
    SELECT T.c1, T.c2 
    INTERSECT 
    SELECT @c1, @c2
);

-- Using separate queries
IF @c1 IS NULL AND @c2 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 IS NULL
    AND T.c2 IS NULL
ELSE IF @c1 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 IS NULL
    AND T.c2 = @c2
ELSE IF @c2 IS NULL
    SELECT * 
    FROM @T AS T
    WHERE T.c1 = @c1
    AND T.c2 IS NULL
ELSE
    SELECT * 
    FROM @T AS T
    WHERE T.c1 = @c1
    AND T.c2 = @c2;

-- Using OPTION (RECOMPILE)
-- Requires 2008 SP1 CU5 or later
SELECT * 
FROM @T AS T
WHERE 
(
    T.c1 = @c1
    OR (@c1 IS NULL AND T.c1 IS NULL)
)
AND 
(
    T.c2 = @c2
    OR (@c2 IS NULL AND T.c2 IS NULL)
)
OPTION (RECOMPILE);
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.