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 column
cá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 IS
so 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 INTERSECT
xâ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);