Permintaan MySQL “NOT IN”

181

Saya ingin menjalankan kueri sederhana untuk memunculkan semua baris di Table1mana nilai kolom utama tidak ada dalam kolom di tabel lain ( Table2).

Saya mencoba menggunakan:

SELECT * FROM Table1 WHERE Table1.principal NOT IN Table2.principal

Ini bukannya melemparkan kesalahan sintaksis. Pencarian Google membawa saya ke forum di mana orang mengatakan bahwa MySQL tidak mendukung NOT INdan sesuatu yang sangat kompleks perlu digunakan. Apakah ini benar? Atau apakah saya membuat kesalahan yang mengerikan?

Kshitij Saxena -KJ-
sumber
1
Dan bagaimana jika saya menginginkan data serupa dari tiga tabel. Maksud saya satu table1 memiliki 2000 entri, dua tabel lainnya 2 & 3 masing-masing mengatakan 500 entri, semuanya memiliki 'nama' bidang yang sama. Bagaimana kita bisa mendapatkan semua detail dari tabel 1 yang tidak ada dalam table2 & 3 berdasarkan 'nama'. Bisakah kita menggunakan TIDAK DALAM dua kali, jika demikian bagaimana ..?

Jawaban:

310

Untuk menggunakan IN, Anda harus memiliki set, gunakan sintaks ini sebagai gantinya:

SELECT * FROM Table1 WHERE Table1.principal NOT IN (SELECT principal FROM table2)
Julien Lebosquain
sumber
85
Hati-hati kapan table2.principalbisa NULL. Dalam hal itu NOT INakan selalu kembali FALSEkarena NOT INdiperlakukan sebagai <> ALL, yang membandingkan semua baris dari subquery seperti Table1.principal <> table2.principal, yang gagal bila dibandingkan dengan NULL: Table1.principal <> NULLtidak akan menghasilkan TRUE. Untuk memperbaiki: NOT IN (SELECT principal FROM table2 WHERE principal IS NOT NULL).
Basti
4
Terima kasih atas komentar @Basti! Menghabiskan banyak waktu untuk mencoba memahami mengapa kueri tidak berfungsi seperti yang diharapkan.
gva
3
Jangan lupa untuk menghindari penggunaan 'SELECT *' di dalam daftar 'NOT IN'. Anda harus memilih kolom tertentu. Kalau tidak, Anda akan mendapatkan kesalahan ini: stackoverflow.com/questions/14046838/…
Lorien Brune
165

Opsi subquery sudah dijawab, tetapi perhatikan bahwa dalam banyak kasus a LEFT JOINbisa menjadi cara yang lebih cepat untuk melakukan ini:

SELECT table1.*
FROM table1 LEFT JOIN table2 ON table2.principal=table1.principal
WHERE table2.principal IS NULL

Jika Anda ingin memeriksa beberapa tabel untuk memastikan tidak ada di salah satu tabel (seperti dalam komentar SRKR), Anda dapat menggunakan ini:

SELECT table1.*
FROM table1
LEFT JOIN table2 ON table2.name=table1.name
LEFT JOIN table3 ON table3.name=table1.name
WHERE table2.name IS NULL AND table3.name IS NULL
Lukáš Lalinský
sumber
2
Dalam pengujian saya sendiri, memiliki kinerja yang sama untuk NOT IN& LEFT JOIN. +1 keduanya
BufferStack
setelah kueri berjalan, Anda harus mendapatkan hasil yang sama tidak peduli apa yang disebabkan oleh caching DB internal
Toote
Bagi saya kinerjanya jauh lebih baik. Saya berlari melalui tabel yang berbeda, sementara mereka memiliki kunci asing ditetapkan.
Keenora Fluffball
36

TIDAK DI vs. TIDAK ADA vs. LEFT BERGABUNG / IS NULL di MySQL

MySQL, serta semua sistem lain kecuali SQL Server, dapat mengoptimalkan LEFT JOIN/IS NULL mengembalikan FALSEsegera setelah nilai yang cocok ditemukan, dan itu adalah satu-satunya sistem yang peduli untuk mendokumentasikan perilaku ini. [...] Karena MySQL tidak dapat menggunakan HASHdan MERGEbergabung dengan algoritma, satu-satunya ANTI JOINyang mampu adalahNESTED LOOPS ANTI JOIN

[...]

Pada dasarnya, [ NOT IN] adalah persis rencana yang sama yang LEFT JOIN/ IS NULLkegunaan, meskipun fakta rencana ini dijalankan oleh cabang yang berbeda dari kode dan mereka terlihat berbeda dalam hasil EXPLAIN. Algoritma pada kenyataannya sama dan kueri lengkap pada saat yang sama.

[...]

Sulit untuk mengatakan alasan yang tepat untuk [penurunan kinerja saat menggunakan NOT EXISTS] , karena penurunan ini linear dan tampaknya tidak bergantung pada distribusi data, jumlah nilai di kedua tabel, dll., Selama kedua bidang diindeks. Karena ada tiga bagian kode di MySQL yang pada dasarnya melakukan satu pekerjaan, ada kemungkinan bahwa kode yang bertanggung jawab untuk EXISTSmembuat semacam pemeriksaan tambahan yang membutuhkan waktu ekstra.

[...]

MySQL dapat mengoptimalkan ketiga metode untuk melakukan semacam NESTED LOOPS ANTI JOIN. […] Namun, ketiga metode ini menghasilkan tiga paket berbeda yang dijalankan oleh tiga kode berbeda. Kode yang mengeksekusi EXISTSpredikat sekitar 30% kurang efisien [...]

Itu sebabnya cara terbaik untuk mencari nilai yang hilang di MySQL menggunakan a LEFT JOIN/ IS NULLatau NOT INbukan NOT EXISTS.

(penekanan ditambahkan)

engin
sumber
7

Sayangnya sepertinya ada masalah dengan penggunaan MySql dari klausa "TIDAK DALAM", pemotretan di bawah ini menunjukkan opsi sub-kueri yang memberikan hasil yang salah:

mysql> show variables like '%version%';
+-------------------------+------------------------------+
| Variable_name           | Value                        |
+-------------------------+------------------------------+
| innodb_version          | 1.1.8                        |
| protocol_version        | 10                           |
| slave_type_conversions  |                              |
| version                 | 5.5.21                       |
| version_comment         | MySQL Community Server (GPL) |
| version_compile_machine | x86_64                       |
| version_compile_os      | Linux                        |
+-------------------------+------------------------------+
7 rows in set (0.07 sec)

mysql> select count(*) from TABLE_A where TABLE_A.Pkey not in (select distinct TABLE_B.Fkey from TABLE_B );
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.07 sec)

mysql> select count(*) from TABLE_A left join TABLE_B on TABLE_A.Pkey = TABLE_B.Fkey where TABLE_B.Pkey is null;
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> select count(*) from TABLE_A where NOT EXISTS (select * FROM TABLE_B WHERE TABLE_B.Fkey = TABLE_A.Pkey );
+----------+
| count(*) |
+----------+
|      139 |
+----------+
1 row in set (0.06 sec)

mysql> 
Legna
sumber
7

Hati-hati NOT INbukan alias untuk <> ANY, tetapi untuk <> ALL!

http://dev.mysql.com/doc/refman/5.0/id/any-in-some-subqueries.html

SELECT c FROM t1 LEFT JOIN t2 USING (c) WHERE t2.c IS NULL

tidak bisa diganti oleh

SELECT c FROM t1 WHERE c NOT IN (SELECT c FROM t2)

Anda harus menggunakan

SELECT c FROM t1 WHERE c <> ANY (SELECT c FROM t2)
pengguna4554358
sumber