Saya akan menulis ulang tes sebagai
IF CASE
WHEN EXISTS (SELECT ...) THEN CASE
WHEN EXISTS (SELECT ...) THEN 1
END
END = 1
Ini menjamin korsleting seperti yang dijelaskan di sini, tetapi ini berarti Anda harus memilih yang termurah untuk dievaluasi terlebih dahulu daripada menyerahkannya kepada pengoptimal.
Dalam pengujian saya yang sangat terbatas di bawah ini, hal berikut tampaknya benar saat menguji
1. EXISTS AND EXISTS
EXISTS AND EXISTS
versi tampaknya paling bermasalah. Rantai ini bersama-sama beberapa semi luar bergabung. Tidak ada kasus yang mengatur ulang urutan tes untuk mencoba dan melakukan yang lebih murah terlebih dahulu (masalah yang dibahas di paruh kedua posting blog ini). Dalam IF ...
versi itu tidak akan ada bedanya jika itu karena tidak korsleting. Namun ketika predikat gabungan ini dimasukkan ke dalam WHERE
klausa rencana berubah dan terjadi korsleting sehingga penataan ulang bisa bermanfaat.
/*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
*/
Rencana untuk semua ini tampak sangat mirip. Alasan perbedaan perilaku antara SELECT 1 WHERE ...
versi dan IF ...
versi adalah bahwa untuk yang pertama jika kondisinya salah maka perilaku yang benar adalah tidak mengembalikan hasil sehingga hanya menghubungkan OUTER SEMI JOINS
dan jika salah satu salah maka baris nol diteruskan ke baris berikutnya.
Namun IF
versi selalu perlu mengembalikan hasil 1 atau nol. Paket ini menggunakan kolom penyelidikan di sambungan luarnya dan menyetelnya ke false jika EXISTS
tes tidak lulus (daripada hanya membuang baris). Ini berarti selalu ada 1 baris yang dimasukkan ke Gabung berikutnya dan selalu dieksekusi.
CASE
versi memiliki rencana yang sangat mirip tetapi menggunakan PASSTHRU
predikat yang digunakan untuk melewati eksekusi JOIN jika sebelumnya THEN
kondisi tidak terpenuhi. Saya tidak yakin mengapa menggabungkan AND
s tidak akan menggunakan pendekatan yang sama.
2. EXISTS OR EXISTS
EXISTS OR EXISTS
versi menggunakan gabungan (UNION ALL
) operator sebagai input dalam ke semi join luar. Pengaturan ini berarti bahwa ia dapat berhenti meminta baris dari sisi dalam segera setelah yang pertama dikembalikan (yaitu dapat secara efektif membuat hubungan pendek) Semua 4 kueri berakhir dengan rencana yang sama di mana predikat yang lebih murah dievaluasi terlebih dahulu.
/*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. Menambahkan ELSE
Terpikir oleh saya untuk mencoba hukum De Morgan untuk mengonversi AND
ke OR
dan lihat apakah itu membuat perbedaan. Mengonversi kueri pertama memberi
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
*/
Jadi ini masih tidak ada bedanya dengan perilaku hubungan arus pendek. Namun jika Anda menghapus NOT
dan membalik urutan IF ... ELSE
kondisikan sekarang tidak korsleting!
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
*/