Penafian: Saya telah menemukan masalah (saya pikir), tetapi saya ingin menambahkan masalah ini ke Stack Overflow karena saya tidak dapat (dengan mudah) menemukannya di mana pun. Juga, seseorang mungkin memiliki jawaban yang lebih baik daripada saya.
Saya memiliki database di mana satu tabel "Biasa" dirujuk oleh beberapa tabel lainnya. Saya ingin melihat catatan apa di tabel Common yatim (yaitu, tidak memiliki referensi dari tabel lain).
Saya menjalankan kueri ini:
select *
from Common
where common_id not in (select common_id from Table1)
and common_id not in (select common_id from Table2)
Saya tahu bahwa ada catatan yatim, tetapi tidak ada catatan yang dikembalikan. Kenapa tidak?
(Ini SQL Server, jika itu penting.)
sql
sql-server
tsql
Jeremy Stein
sumber
sumber
Jawaban:
Memperbarui:
Artikel-artikel ini di blog saya menjelaskan perbedaan antara metode-metode ini secara lebih rinci:
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:SQL Server
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:PostgreSQL
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:Oracle
NOT IN
vs.NOT EXISTS
vsLEFT JOIN / IS NULL
.:MySQL
Ada tiga cara untuk melakukan permintaan seperti itu:
LEFT JOIN / IS NULL
:NOT EXISTS
:NOT IN
:Ketika
table1.common_id
tidak dapat dibatalkan, semua kueri ini secara semantik sama.Ketika nullable,
NOT IN
berbeda, karenaIN
(dan, karenanya,NOT IN
) kembaliNULL
ketika nilai tidak cocok dengan apa pun dalam daftar yang berisi aNULL
.Ini mungkin membingungkan tetapi mungkin menjadi lebih jelas jika kita mengingat sintaks alternatif untuk ini:
Hasil dari kondisi ini adalah produk boolean dari semua perbandingan dalam daftar. Tentu saja,
NULL
nilai tunggal menghasilkanNULL
hasil yang menjadikan keseluruhan hasilNULL
juga.Kita tidak pernah bisa mengatakan hal itu dengan pasti
common_id
itu tidak sama dengan apa pun dari daftar ini, karena setidaknya salah satu nilainyaNULL
.Misalkan kita memiliki data ini:
LEFT JOIN / IS NULL
danNOT EXISTS
akan kembali3
, tidakNOT IN
akan mengembalikan apa pun (karena akan selalu dievaluasi jugaFALSE
atauNULL
).Dalam
MySQL
, dalam kasus pada kolom non-nullable,LEFT JOIN / IS NULL
danNOT IN
sedikit (beberapa persen) lebih efisien daripadaNOT EXISTS
. Jika kolom dapat dibatalkan,NOT EXISTS
apakah yang paling efisien (sekali lagi, tidak banyak).Dalam
Oracle
, ketiga pertanyaan menghasilkan rencana yang sama (aANTI JOIN
).Di
SQL Server
,NOT IN
/NOT EXISTS
lebih efisien, karenaLEFT JOIN / IS NULL
tidak dapat dioptimalkan keANTI JOIN
oleh pengoptimalnya.Di
PostgreSQL
,LEFT JOIN / IS NULL
danNOT EXISTS
lebih efisien daripadaNOT IN
, mereka dioptimalkan untukAnti Join
, sementaraNOT IN
menggunakanhashed subplan
(atau bahkan dataransubplan
jika subquery terlalu besar untuk hash)sumber
NOT EXISTS
mengevaluasi ke TRUE jika kueri di dalamnya mengembalikan setiap baris.SELECT NULL
bisa jadiSELECT *
atauSELECT 1
atau apa pun,NOT EXISTS
predikat tidak melihat nilai-nilai baris, hanya menghitungnya.Jika Anda ingin dunia menjadi tempat boolean dua nilai, Anda harus mencegah sendiri kasus nol (nilai ketiga).
Jangan menulis klausa IN yang memungkinkan null di sisi daftar. Saring mereka!
sumber
common_id not in
, kita masih dapat memilikicommon_id
nilai ituNULL
. Jadi bukankah masalah tidak mendapatkan hasil masih bertahan?Table1 atau Table2 memiliki beberapa nilai null untuk common_id. Gunakan kueri ini sebagai gantinya:
sumber
sumber
Tak jauh dari kepala saya ...
Saya menjalankan beberapa tes dan inilah hasil jawaban wrt @ patmortech dan komentar @ rexem.
Jika Table1 atau Table2 tidak diindeks pada commonID, Anda mendapatkan pemindaian tabel tetapi permintaan @ patmortech masih dua kali lebih cepat (untuk tabel master baris 100 ribu).
Jika tidak ada yang diindeks pada commonID, Anda mendapatkan dua pemindaian tabel dan perbedaannya dapat diabaikan.
Jika keduanya diindeks pada commonID, kueri "tidak ada" berjalan pada 1/3 waktu.
sumber
sumber
Anggaplah nilai-nilai ini untuk common_id:
Kami ingin baris dalam Common untuk kembali, karena tidak ada di salah satu tabel lainnya. Namun, null melempar kunci inggris.
Dengan nilai-nilai itu, kueri setara dengan:
Itu setara dengan:
Di sinilah masalahnya dimulai. Saat membandingkan dengan nol, jawabannya tidak diketahui . Jadi kueri berkurang menjadi
salah atau tidak dikenal tidak diketahui:
true dan not unkown juga tidak dikenal:
Kondisi di mana tidak mengembalikan catatan di mana hasilnya tidak diketahui, jadi kami tidak mendapatkan catatan kembali.
Salah satu cara untuk mengatasi ini adalah dengan menggunakan operator yang ada daripada masuk. Ada yang tidak pernah mengembalikan unkown karena beroperasi pada baris daripada kolom. (Baris ada atau tidak; tidak ada satupun ambiguitas nol ini di level baris!)
sumber
ini bekerja untuk saya :)
sumber
NOT IN
sana?sumber
Saya punya contoh di mana saya melihat ke atas dan karena satu meja memegang nilai sebagai ganda, yang lain sebagai string, mereka tidak akan cocok (atau tidak cocok tanpa pemain). Tapi hanya TIDAK DI . Sebagai SELECT ... DI ... bekerja. Aneh, tapi kupikir aku akan berbagi kalau-kalau ada orang lain yang menemukan perbaikan sederhana ini.
sumber
Silakan ikuti contoh di bawah ini untuk memahami topik di atas:
Anda juga dapat mengunjungi tautan berikut untuk mengetahui Anti gabung
Tetapi jika kita menggunakan
NOT IN
dalam kasus itu kita tidak mendapatkan data apa pun.Ini terjadi karena (
select department_id from hr.employees
) mengembalikan nilai nol dan seluruh kueri dievaluasi sebagai salah. Kita dapat melihatnya jika kita mengubah SQL sedikit seperti di bawah ini dan menangani nilai nol dengan fungsi NVL.Sekarang kami mendapatkan data:
Sekali lagi kami mendapatkan data karena kami telah menangani nilai nol dengan fungsi NVL.
sumber