Masalah ini muncul ketika saya mendapat catatan yang berbeda menghitung untuk apa yang saya pikir adalah pertanyaan yang identik satu menggunakan not in
where
kendala dan yang lainnya a left join
. Tabel dalam not in
batasan memiliki satu nilai nol (data buruk) yang menyebabkan kueri untuk mengembalikan hitungan 0 catatan. Saya agak mengerti mengapa tetapi saya bisa menggunakan bantuan untuk sepenuhnya memahami konsep ini.
Untuk menyatakannya secara sederhana, mengapa kueri A mengembalikan hasil tetapi B tidak?
A: select 'true' where 3 in (1, 2, 3, null)
B: select 'true' where 3 not in (1, 2, null)
Ini di SQL Server 2005. Saya juga menemukan bahwa panggilan set ansi_nulls off
menyebabkan B mengembalikan hasil.
NOT IN
serangkaian<> and
perubahan perilaku semantik tidak pada set ini menjadi sesuatu yang lain?SELECT 1 WHERE NULL NOT IN (SELECT 1 WHERE 1=0);
menghasilkan baris alih-alih hasil kosong yang saya harapkan.Setiap kali Anda menggunakan NULL Anda benar-benar berurusan dengan logika Tiga Nilai.
Kueri pertama Anda mengembalikan hasil ketika klausa WHERE mengevaluasi ke:
Yang kedua:
UNKNOWN tidak sama dengan FALSE, Anda dapat dengan mudah mengujinya dengan menelepon:
Kedua pertanyaan tidak akan memberi Anda hasil
Jika UNKNOWN sama dengan FALSE maka dengan asumsi bahwa kueri pertama akan memberi Anda SALAH, yang kedua harus mengevaluasi ke TRUE karena akan sama dengan TIDAK (FALSE).
Bukan itu masalahnya.
Ada artikel yang sangat bagus tentang hal ini di SqlServerCentral .
Seluruh masalah NULLs dan Three-Valued Logic dapat sedikit membingungkan pada awalnya tetapi penting untuk dipahami untuk menulis kueri yang benar di TSQL
Artikel lain yang saya rekomendasikan adalah Fungsi SQL Agregat dan NULL .
sumber
NOT IN
mengembalikan 0 catatan bila dibandingkan dengan nilai yang tidak diketahuiKarena
NULL
tidak diketahui,NOT IN
kueri yang berisi aNULL
atauNULL
s dalam daftar nilai yang mungkin akan selalu mengembalikan0
catatan karena tidak ada cara untuk memastikan bahwaNULL
nilainya bukan nilai yang sedang diuji.sumber
Bandingkan dengan nol tidak ditentukan, kecuali jika Anda menggunakan IS NULL.
Jadi, ketika membandingkan 3 dengan NULL (permintaan A), ia mengembalikan tidak terdefinisi.
Yaitu SELECT 'true' di mana 3 in (1,2, null) dan SELECT 'true' di mana 3 in (1,2, null)
akan menghasilkan hasil yang sama, karena TIDAK (TIDAK DIKENAL) masih tidak didefinisikan, tetapi tidak BENAR
sumber
Judul pertanyaan ini pada saat penulisan adalah
Dari teks pertanyaan tampak bahwa masalah itu terjadi dalam
SELECT
permintaan SQL DML , bukan SQL DDLCONSTRAINT
.Namun, terutama mengingat kata-kata dari judulnya, saya ingin menunjukkan bahwa beberapa pernyataan yang dibuat di sini berpotensi pernyataan yang menyesatkan, yaitu yang sejalan dengan (parafrase)
Meskipun ini adalah kasus untuk DML SQL, ketika mempertimbangkan kendala efeknya berbeda.
Pertimbangkan tabel yang sangat sederhana ini dengan dua kendala yang diambil langsung dari predikat dalam pertanyaan (dan ditangani dalam jawaban yang sangat baik oleh @Brannon):
Sesuai jawaban @ Brannon, kendala pertama (menggunakan
IN
) mengevaluasi ke TRUE dan kendala kedua (menggunakanNOT IN
) mengevaluasi ke UNKNOWN. Namun , sisipan berhasil! Oleh karena itu, dalam hal ini tidak sepenuhnya benar untuk mengatakan, "Anda tidak mendapatkan baris" karena kami memang mendapat baris yang dimasukkan sebagai hasilnya.Efek di atas memang yang benar sehubungan dengan SQL-92 Standard. Bandingkan dan bandingkan bagian berikut dari SQL-92 spec
Dengan kata lain:
Dalam SQL DML, baris dihapus dari hasil ketika
WHERE
mengevaluasi ke UNKNOWN karena tidak memenuhi kondisi "benar".Dalam SQL DDL (yaitu kendala), baris tidak dihapus dari hasil ketika mereka mengevaluasi untuk UNKNOWN karena tidak memenuhi kondisi "tidak salah".
Meskipun efek dalam SQL DML dan SQL DDL masing-masing mungkin tampak kontradiktif, ada alasan praktis untuk memberikan hasil UNKNOWN 'manfaat keraguan' dengan memungkinkan mereka untuk memenuhi kendala (lebih tepat, memungkinkan mereka untuk tidak gagal memenuhi kendala) : tanpa perilaku ini, setiap kendala harus secara eksplisit menangani nol dan itu akan sangat tidak memuaskan dari perspektif desain bahasa (belum lagi, rasa sakit yang tepat untuk coders!)
ps jika Anda menemukan itu sebagai tantangan untuk mengikuti logika seperti "tidak diketahui tidak gagal untuk memenuhi kendala" karena saya menulisnya, maka pertimbangkan Anda dapat membuang semua ini hanya dengan menghindari kolom nullable dalam SQL DDL dan apa pun dalam SQL DML yang menghasilkan nulls (misalnya gabungan luar)!
sumber
NOT IN (subquery)
melibatkan nol dapat memberikan hasil yang tidak terduga, tergoda untuk menghindariIN (subquery)
sepenuhnya dan selalu menggunakanNOT EXISTS (subquery)
(seperti yang pernah saya lakukan!) Karena tampaknya selalu menangani nol dengan benar. Namun, ada beberapa kasus di manaNOT IN (subquery)
memberikan hasil yang diharapkan sedangkanNOT EXISTS (subquery)
memberikan hasil yang tidak terduga! Saya mungkin belum menulis ini jika saya dapat menemukan catatan saya pada subjek (perlu catatan karena tidak intuitif!) Kesimpulannya sama, namun: hindari nol!TRUE
,FALSE
danUNKNOWN
. Saya kira 4.10 dapat membaca, "Kendala pemeriksaan tabel terpenuhi jika dan hanya jika kondisi pencarian yang ditentukan adalah BENAR atau TIDAK DIKETAHUI untuk setiap baris tabel" - perhatikan perubahan pada akhir kalimat - yang Anda hapus - - dari "untuk apa pun" ke "untuk semua". Saya merasa perlu untuk memanfaatkan nilai-nilai logis karena makna 'benar' dan 'salah' dalam bahasa alami pastilah merujuk pada logika dua nilai klasik.CREATE TABLE T ( a INT NOT NULL UNIQUE, b INT CHECK( a = b ) );
- maksudnya di sini adalahb
harus samaa
atau nol. Jika suatu kendala harus menghasilkan TRUE untuk dipenuhi maka kita perlu mengubah kendala untuk secara eksplisit menangani nulls misalnyaCHECK( a = b OR b IS NULL )
. Dengan demikian, setiap kendala harus memiliki...OR IS NULL
logika yang ditambahkan oleh pengguna untuk setiap kolom nullable yang terlibat: lebih rumit, lebih banyak bug ketika mereka lupa melakukannya, dll. Jadi saya pikir komite standar SQL hanya mencoba untuk menjadi pragmatis.Dalam A, 3 diuji untuk kesetaraan terhadap setiap anggota set, menghasilkan (FALSE, FALSE, TRUE, UNKNOWN). Karena salah satu elemennya BENAR, maka kondisinya BENAR. (Mungkin juga terjadi hubungan arus pendek di sini, jadi itu benar-benar berhenti segera setelah menyentuh TRUE pertama dan tidak pernah mengevaluasi 3 = NULL.)
Dalam B, saya pikir itu mengevaluasi kondisi sebagai TIDAK (3 in (1,2, null)). Menguji 3 untuk kesetaraan terhadap hasil yang ditetapkan (FALSE, FALSE, UNKNOWN), yang dikumpulkan ke UNKNOWN. TIDAK (TIDAK DIKENALKAN) menghasilkan TIDAK DIKETAHUI. Jadi secara keseluruhan kebenaran kondisi tersebut tidak diketahui, yang pada dasarnya diperlakukan sebagai SALAH.
sumber
Dapat disimpulkan dari jawaban di sini yang
NOT IN (subquery)
tidak menangani nulls dengan benar dan harus dihindariNOT EXISTS
. Namun, kesimpulan seperti itu mungkin prematur. Dalam skenario berikut, dikreditkan ke Chris Date (Pemrograman dan Desain Basis Data, Vol 2 No 9, September 1989), ituNOT IN
yang menangani nulls dengan benar dan mengembalikan hasil yang benar, daripadaNOT EXISTS
.Pertimbangkan tabel
sp
untuk mewakili pemasok (sno
) yang dikenal memasok bagian (pno
) dalam jumlah (qty
). Tabel saat ini memuat nilai-nilai berikut:Perhatikan bahwa kuantitas dapat dibatalkan yaitu untuk dapat mencatat fakta bahwa pemasok diketahui memasok suku cadang meskipun tidak diketahui dalam jumlah berapa.
Tugasnya adalah menemukan pemasok yang dikenal sebagai nomor bagian persediaan 'P1' tetapi tidak dalam jumlah 1000.
Penggunaan berikut
NOT IN
untuk mengidentifikasi dengan benar pemasok 'S2' saja:Namun, kueri di bawah ini menggunakan struktur umum yang sama tetapi dengan
NOT EXISTS
tetapi salah memasukkan pemasok 'S1' dalam hasilnya (yaitu yang kuantitasnya nol):Jadi
NOT EXISTS
bukan peluru perak yang mungkin muncul!Tentu saja, sumber masalahnya adalah keberadaan nulls, oleh karena itu solusi 'nyata' adalah untuk menghilangkan nulls tersebut.
Ini dapat dicapai (di antara kemungkinan desain lainnya) menggunakan dua tabel:
sp
pemasok yang diketahui memasok suku cadangspq
pemasok diketahui memasok suku cadang dalam jumlah yang dikenalmencatat mungkin harus ada batasan kunci asing di mana
spq
referensisp
.Hasilnya kemudian dapat diperoleh dengan menggunakan operator relasional 'minus' (menjadi
EXCEPT
kata kunci dalam SQL Standar) misalnyasumber
Null menandakan dan tidak adanya data, yaitu tidak diketahui, bukan nilai data apa pun. Sangat mudah bagi orang-orang dari latar belakang pemrograman untuk membingungkan ini karena dalam bahasa tipe C saat menggunakan pointer nol memang bukan apa-apa.
Oleh karena itu dalam kasus pertama 3 memang di set (1,2,3, null) jadi benar dikembalikan
Namun dalam kedua Anda bisa menguranginya menjadi
pilih 'true' di mana 3 tidak dalam (null)
Jadi tidak ada yang dikembalikan karena parser tidak tahu tentang set yang Anda bandingkan - itu bukan set kosong tapi set yang tidak diketahui. Menggunakan (1, 2, null) tidak membantu karena set (1,2) jelas salah, tapi kemudian Anda dan itu menentang yang tidak diketahui, yang tidak diketahui.
sumber
JIKA Anda ingin memfilter dengan NOT IN untuk subquery containg NULLs justcheck for not null
sumber
ini untuk Boy:
ini berfungsi terlepas dari pengaturan ansi
sumber
SQL menggunakan logika bernilai tiga untuk nilai kebenaran. The
IN
permintaan menghasilkan hasil yang diharapkan:Tetapi menambahkan
NOT
tidak membalikkan hasilnya:Ini karena kueri di atas setara dengan yang berikut:
Berikut adalah bagaimana klausa dievaluasi:
Perhatikan itu:
NULL
hasilUNKNOWN
OR
ekspresi di mana tak satu pun dari operanTRUE
dan setidaknya satu operan adalahUNKNOWN
hasilUNKNOWN
( ref )NOT
dariUNKNOWN
hasilUNKNOWN
( ref )Anda dapat memperluas contoh di atas hingga lebih dari dua nilai (mis. NULL, 1 dan 2) tetapi hasilnya akan sama: jika salah satu dari nilai tersebut
NULL
maka tidak ada baris yang cocok.sumber
juga ini mungkin berguna untuk mengetahui perbedaan logis antara bergabung, ada dan di http://weblogs.sqlteam.com/mladenp/archive/2007/05/18/60210.aspx
sumber