Saya selalu default ke NOT EXISTS
.
Rencana eksekusi mungkin sama saat ini tetapi jika salah satu kolom diubah di masa mendatang untuk mengizinkan NULL
adalah NOT IN
versi perlu melakukan lebih banyak pekerjaan (bahkan jika tidak ada NULL
s sebenarnya ada dalam data) dan semantik NOT IN
jika NULL
s adalah hadiah tidak mungkin menjadi yang Anda inginkan.
Bila tidak ada Products.ProductID
atau [Order Details].ProductID
izinkan NULL
adalah NOT IN
akan diperlakukan sama dengan kueri berikut.
SELECT ProductID,
ProductName
FROM Products p
WHERE NOT EXISTS (SELECT *
FROM [Order Details] od
WHERE p.ProductId = od.ProductId)
Paket yang tepat dapat bervariasi tetapi untuk contoh data saya, saya mendapatkan yang berikut ini.
Kesalahpahaman yang cukup umum tampaknya bahwa sub kueri yang berkorelasi selalu "buruk" dibandingkan dengan gabungan. Mereka pasti bisa ketika mereka memaksa rencana loop bersarang (sub query dievaluasi baris demi baris) tetapi rencana ini menyertakan operator logika anti semi join. Penggabungan anti semi tidak terbatas pada loop bersarang tetapi dapat menggunakan hash atau gabungan (seperti dalam contoh ini) juga.
/*Not valid syntax but better reflects the plan*/
SELECT p.ProductID,
p.ProductName
FROM Products p
LEFT ANTI SEMI JOIN [Order Details] od
ON p.ProductId = od.ProductId
Jika [Order Details].ProductID
adalah NULL
-bisa querynya jadi
SELECT ProductID,
ProductName
FROM Products p
WHERE NOT EXISTS (SELECT *
FROM [Order Details] od
WHERE p.ProductId = od.ProductId)
AND NOT EXISTS (SELECT *
FROM [Order Details]
WHERE ProductId IS NULL)
Alasan untuk ini adalah bahwa semantik yang benar jika [Order Details]
berisi NULL
ProductId
s adalah untuk tidak mengembalikan hasil. Lihat ekstra anti semi join dan spool jumlah baris untuk memverifikasi ini yang ditambahkan ke paket.
Jika Products.ProductID
juga diubah menjadi NULL
-bisa querynya jadi
SELECT ProductID,
ProductName
FROM Products p
WHERE NOT EXISTS (SELECT *
FROM [Order Details] od
WHERE p.ProductId = od.ProductId)
AND NOT EXISTS (SELECT *
FROM [Order Details]
WHERE ProductId IS NULL)
AND NOT EXISTS (SELECT *
FROM (SELECT TOP 1 *
FROM [Order Details]) S
WHERE p.ProductID IS NULL)
Alasan untuk itu adalah karena NULL
Products.ProductId
tidak boleh dikembalikan dalam hasil kecuali jika NOT IN
sub kueri tidak memberikan hasil sama sekali (yaitu [Order Details]
meja kosong). Dalam hal ini seharusnya. Dalam rencana untuk sampel data saya ini diterapkan dengan menambahkan anti semi join lainnya seperti di bawah ini.
Efek dari ini ditunjukkan dalam posting blog yang sudah ditautkan oleh Buckley. Dalam contoh di sana, jumlah pembacaan logis meningkat dari sekitar 400 menjadi 500.000.
Selain itu fakta bahwa satu NULL
dapat mengurangi jumlah baris menjadi nol membuat estimasi kardinalitas menjadi sangat sulit. Jika SQL Server mengasumsikan bahwa ini akan terjadi tetapi sebenarnya tidak ada NULL
baris dalam data sisa rencana eksekusi mungkin menjadi bencana yang lebih buruk, jika ini hanya bagian dari kueri yang lebih besar, dengan loop bersarang yang tidak sesuai yang menyebabkan eksekusi berulang dari sub pohon yang mahal misalnya.
Ini bukan satu-satunya rencana eksekusi yang mungkin untuk NOT IN
pada NULL
kolom -mampu namun. Artikel ini menunjukkan satu lagi untuk kueri terhadap AdventureWorks2008
basis data.
Untuk kode NOT IN
pada NOT NULL
kolom atau NOT EXISTS
terhadap kolom nullable atau non nullable itu memberikan rencana berikut.
Ketika kolom berubah menjadi NULL
-bisa NOT IN
rencana sekarang terlihat seperti
Ini menambahkan operator bergabung dalam ekstra untuk rencana tersebut. Perangkat ini dijelaskan di sini. Semuanya ada di sana untuk mengonversi pencarian indeks berkorelasi tunggal sebelumnya di Sales.SalesOrderDetail.ProductID = <correlated_product_id>
untuk dua pencarian per baris luar. Yang tambahan ada di WHERE Sales.SalesOrderDetail.ProductID IS NULL
.
Karena ini berada di bawah anti semi join jika yang mengembalikan baris apa pun, pencarian kedua tidak akan terjadi. Namun jika Sales.SalesOrderDetail
tidak mengandung NULL
ProductID
s itu akan menggandakan jumlah operasi pencarian yang diperlukan.