Tôi sẽ viết lại bài kiểm tra thành
IF CASE
WHEN EXISTS (SELECT ...) THEN CASE
WHEN EXISTS (SELECT ...) THEN 1
END
END = 1
Điều này đảm bảo đoản mạch như được mô tả ở đây nhưng không có nghĩa là bạn cần chọn cái rẻ nhất để đánh giá trước thay vì để nó cho người tối ưu hóa.
Trong các thử nghiệm cực kỳ hạn chế của tôi bên dưới, điều sau dường như đúng khi thử nghiệm
1. EXISTS AND EXISTS
EXISTS AND EXISTS
phiên bản có vẻ có vấn đề nhất. Chuỗi này kết hợp một số bán bên ngoài tham gia. Không có trường hợp nào nó sắp xếp lại thứ tự của các bài kiểm tra để thử và làm bài rẻ hơn trước (một vấn đề được thảo luận trong nửa sau của bài đăng trên blog này). Trong IF ...
phiên bản nó sẽ không tạo ra bất kỳ sự khác biệt nào nếu nó có vì nó không bị đoản mạch. Tuy nhiên, khi vị từ kết hợp này được đặt trong WHERE
điều khoản kế hoạch thay đổi và nó không ngắn mạch để việc sắp xếp lại có thể có lợi.
/*All tests are testing "If False And False"*/
IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
PRINT 'Y'
/*
Table 'spt_values'. Scan count 1, logical reads 9
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
PRINT 'Y'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/
SELECT 1
WHERE EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
AND EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)
AND EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_values'. Scan count 1, logical reads 9
*/
Các kế hoạch cho tất cả những thứ này có vẻ rất giống nhau. Lý do cho sự khác biệt về hành vi giữa SELECT 1 WHERE ...
phiên bản và IF ...
phiên bản dành cho điều kiện trước đây nếu điều kiện sai thì hành vi đúng là không trả về kết quả nên nó chỉ chuỗi OUTER SEMI JOINS
và nếu một sai thì hàng không sẽ chuyển sang hàng tiếp theo.
Tuy nhiên, IF
phiên bản luôn luôn cần trả về kết quả là 1 hoặc 0. Kế hoạch này sử dụng một cột thăm dò trong các phép nối bên ngoài của nó và đặt giá trị này thành false nếu EXISTS
kiểm tra không được thông qua (thay vì chỉ đơn giản là loại bỏ hàng). Điều này có nghĩa là luôn có 1 hàng đưa vào Tham gia tiếp theo và nó luôn được thực thi.
CASE
phiên bản có gói rất giống nhưng sử dụng PASSTHRU
vị từ mà nó sử dụng để bỏ qua việc thực thi JOIN nếu THEN
trước đó điều kiện không được đáp ứng. Tôi không chắc tại sao lại kết hợp AND
s sẽ không sử dụng cùng một cách tiếp cận.
2. EXISTS OR EXISTS
EXISTS OR EXISTS
phiên bản sử dụng cách nối (UNION ALL
) làm đầu vào bên trong cho phép nối bán bên ngoài. Sự sắp xếp này có nghĩa là nó có thể ngừng yêu cầu các hàng từ phía bên trong ngay sau khi hàng đầu tiên được trả lại (tức là nó có thể gây ngắn mạch hiệu quả) Tất cả 4 truy vấn đều kết thúc với cùng một kế hoạch trong đó vị từ rẻ hơn được đánh giá đầu tiên.
/*All tests are testing "If True Or True"*/
IF EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1)
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
IF EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1)
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)= 1)
OR EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
SELECT 1
WHERE EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)<>1)
OR EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=1)
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/
3. Thêm ELSE
Tôi đã xảy ra khi thử định luật De Morgan để chuyển đổi AND
thành OR
và xem điều đó có tạo ra sự khác biệt nào không. Chuyển đổi truy vấn đầu tiên mang lại
IF NOT ((NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1)))
PRINT 'Y'
ELSE
PRINT 'N'
/*
Table 'spt_monitor'. Scan count 1, logical reads 1
Table 'spt_values'. Scan count 1, logical reads 9
*/
Vì vậy, điều này vẫn không tạo ra bất kỳ sự khác biệt nào đối với hành vi đoản mạch. Tuy nhiên, nếu bạn xóa NOT
và đảo ngược thứ tự của IF ... ELSE
điều kiện bây giờ hiện ngắn mạch!
IF (NOT EXISTS(SELECT COUNT(*) FROM master..spt_monitor HAVING COUNT(*)=2)
OR NOT EXISTS (SELECT COUNT(*) FROM master..spt_values HAVING COUNT(*)=1))
PRINT 'N'
ELSE
PRINT 'Y'
/*
Table 'Worktable'. Scan count 0, logical reads 0
Table 'spt_monitor'. Scan count 1, logical reads 1
*/