KECUALI operator vs TIDAK DALAM

19

The EXCEPTOperator diperkenalkan pada SQL Server 2005 tapi apa perbedaan antara NOT INdan EXCEPT?

Apakah itu melakukan hal yang sama? Saya ingin penjelasan sederhana dengan contoh.

Heisenberg
sumber

Jawaban:

29

Ada dua perbedaan utama antara EXCEPTdan NOT IN.

KECUALI

EXCEPTmemfilter DISTINCTnilai dari tabel sebelah kiri yang tidak muncul di tabel sebelah kanan. Ini pada dasarnya sama dengan melakukan NOT EXISTSdengan DISTINCTklausa.

Itu juga mengharapkan dua tabel (atau subset kolom dari tabel) untuk memiliki jumlah kolom yang sama di sisi kiri dan kanan kueri

Misalnya, Anda tidak dapat melakukan:

SELECT ID, Name FROM TableA
EXCEPT
SELECT ID FROM TableB

Ini akan menghasilkan kesalahan:

Semua kueri yang digabungkan menggunakan operator UNION, INTERSECT atau EXCEPT harus memiliki jumlah ekspresi yang sama dalam daftar target mereka.

TIDAK MASUK

NOT INtidak memfilter untuk DISTINCTnilai dan mengembalikan semua nilai dari tabel sebelah kiri yang tidak muncul di tabel sebelah kanan.

NOT IN mengharuskan Anda membandingkan satu kolom dari satu tabel dengan satu kolom dari tabel atau subquery lain.

Misalnya, jika subquery Anda mengembalikan banyak kolom:

SELECT * FROM TableA AS nc
WHERE ID NOT IN (SELECT ID, Name FROM TableB AS ec)

Anda akan mendapatkan kesalahan berikut:

Hanya satu ekspresi yang dapat ditentukan dalam daftar pilih ketika subquery tidak diperkenalkan dengan EXISTS.

Namun, jika tabel sebelah kanan berisi NULLnilai yang sedang difilter oleh NOT IN, set hasil kosong dikembalikan, berpotensi memberikan hasil yang tidak terduga.

CONTOH

CREATE TABLE #NewCustomers (ID INT);
CREATE TABLE #ExistingCustomers (ID INT);

INSERT INTO #NewCustomers
        ( ID )
VALUES
     (8), (9), (10), (1), (3), (8);

INSERT INTO #ExistingCustomers
        ( ID )
VALUES
        ( 1) , (2), (3), (4), (5);


-- EXCEPT filters for DISTINCT values
SELECT * FROM #NewCustomers AS nc
EXCEPT
SELECT * FROM #ExistingCustomers AS ec

-- NOT IN returns all values without filtering
SELECT * FROM #NewCustomers AS nc
WHERE ID NOT IN (SELECT ID FROM #ExistingCustomers AS ec)

Dari dua kueri di atas, EXCEPTkembalikan 3 baris dari #NewCustomers, saring 1 dan 3 yang cocok #ExistingCustomersdan duplikat 8.

NOT INtidak melakukan penyaringan yang berbeda ini dan mengembalikan 4 baris dari #NewCustomersdengan duplikat 8.

Jika sekarang kita menambahkan a NULLke #ExistingCustomerstabel, kita melihat hasil yang sama dikembalikan oleh EXCEPT, namun NOT INakan mengembalikan set hasil kosong.

INSERT INTO #ExistingCustomers
        ( ID )
VALUES
        ( NULL );

-- With NULL values in the right-hand table, EXCEPT still returns the same results as above
SELECT * FROM #NewCustomers AS nc
EXCEPT
SELECT * FROM #ExistingCustomers AS ec

-- NOT IN now returns no results
SELECT * FROM #NewCustomers AS nc
WHERE ID NOT IN (SELECT ID FROM #ExistingCustomers AS ec)

DROP TABLE #NewCustomers;
DROP TABLE #ExistingCustomers;

Alih-alih NOT IN, Anda harus benar-benar melihat NOT EXISTSdan ada perbandingan yang bagus antara keduanya di blog Gail Shaw .

Mark Sinkinson
sumber
Apakah KECUALI akan menggunakan indeks jika sesuai?
JohnOpincar
1

Sebuah tambahan untuk komentar luar biasa Mark Sinkinson:

NOT IN mengharuskan Anda membandingkan satu kolom dari satu tabel dengan satu kolom dari tabel atau subquery lain.

Anda sebenarnya bisa tampil NOT INdengan lebih dari satu kolom.
Misalnya ini adalah permintaan SQL * prefectly legal :

SELECT  E.first_name, E.last_name
FROM    employees E
WHERE   (E.first_name, E.last_name) NOT IN 
              (SELECT M.first_name, M.last_name FROM managers M)

Yang akan kembali first_namedan last_namesemua orang yang adalah karyawan, tetapi tidak juga manajer.

*: tetapi konstruksinya belum diimplementasikan dalam SQL Server.

Michael Vigato
sumber
-2

NOT IN di atas gagal karena harus ada korelasi antara predikat dalam permintaan utama dan subquery. Jika Anda membiarkannya, Anda akan mendapatkan subquery yang tidak terkorelasi.

PILIH * DARI TableA SEBAGAI nc ID MANA TIDAK DI (SELECT ID, Nama DARI TableB SEBAGAI ec mana nc.ID = ec.ID)

KECUALI lebih baik dan akan menangani setiap baris nol tanpa menggunakan predikat IS NULL / IS NOT NULL.

Burton R Leed
sumber