PostgreSQL 'NOT IN' dan subquery

91

Saya mencoba menjalankan kueri ini:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (select consols.mac from consols)

Tapi saya tidak mendapatkan hasil. Saya mengujinya, dan saya tahu ada yang salah dengan sintaksnya. Di MySQL, kueri seperti itu berfungsi dengan sempurna. Saya telah menambahkan baris untuk memastikan bahwa ada baris macyang tidak ada di consolstabel, tetapi tetap tidak memberikan hasil apa pun.

garis skowron
sumber
4
Apakah consols.mackolom NULLatau NOT NULL?
Mark Byers

Jawaban:

169

Saat menggunakan NOT IN, Anda harus memastikan bahwa tidak ada nilai yang NULL:

SELECT mac, creation_date 
FROM logs 
WHERE logs_type_id=11
AND mac NOT IN (
    SELECT mac
    FROM consols
    WHERE mac IS NOT NULL -- add this
)
Mark Byers
sumber
4
Catatan: WHERE mac IS NOT NULLklausa di subkueri tidak diperlukan, karena In(...)selalu menghapus NULL (dan duplikat). Karena satu set tidak dapat berisi NULLs
wildplasser
7
@wildplasser Saya tidak tahu tentang itu. Itu tidak berhasil untuk saya, sampai saya menambahkan IS NOT NULL. Yang bersarang SELECTkembali beberapa NULLS, dan itu tersandung IN(SELECT...).
robins35
2
Saya akan sangat menghargai penjelasan mengapa IS NOT NULLpenyebab ini berhasil.
mbarkhau
7
Tampaknya penggunaan NULLdalam NOT INklausa tidak berfungsi karena perbandingannya NULLtidak benar atau salah. sqlbadpractices.com/using-not-in-operator-with-null-values
mbarkhau
2
Kueri tidak akan mengembalikan baris is not nulljika tidak ada subkueri jika tidak menghasilkan nilai yang cocok dan setidaknya satu nullnilai. Dari bagian 9.22 dari manual PostgreSQL saat ini (versi 10): "[…] jika tidak ada nilai di sisi kanan yang sama dan setidaknya satu baris di sisi kanan menghasilkan null, hasil dari konstruksi NOT IN akan menjadi null, tidak benar . "
Christopher Lewis
29

Saat menggunakan NOT IN, Anda juga harus mempertimbangkan NOT EXISTS, yang menangani kasus null secara diam-diam. Lihat juga Wiki PostgreSQL

SELECT mac, creation_date 
FROM logs lo
WHERE logs_type_id=11
AND NOT EXISTS (
  SELECT *
  FROM consols nx
  WHERE nx.mac = lo.mac
  );
wildplasser
sumber
3
Perhatikan juga kehilangan kinerja yang sangat besar saat menggunakan NOT EXISTSvs... NOT IN
IcanDivideBy0
1
@ IcanDivideBy0 Dalam kebanyakan kasus, mereka menghasilkan rencana kueri yang sama. Sudahkah Anda mengujinya?
wildplasser
1
Ada juga peningkatan kinerja dalam menggunakan NOT IN dan bukannya NOT EXISTS, dalam kasus subkueri. Lihat wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_NOT_IN
Gerbrand
8

Anda juga bisa menggunakan kondisi LEFT JOIN dan IS NULL:

SELECT 
  mac, 
  creation_date 
FROM 
  logs
    LEFT JOIN consols ON logs.mac = consols.mac
WHERE 
  logs_type_id=11
AND
  consols.mac IS NULL;

Indeks pada kolom "mac" mungkin meningkatkan kinerja.

Frank Heikens
sumber