Praktik terbaik antara menggunakan LEFT JOIN atau NOT EXISTS

67

Apakah ada praktik terbaik antara menggunakan LEFT JOIN atau format NOT EXISTS?

Apa manfaat menggunakan yang satu dari yang lain?

Jika tidak ada, yang mana yang lebih disukai?

SELECT *
FROM tableA A
LEFT JOIN tableB B
     ON A.idx = B.idx
WHERE B.idx IS NULL

SELECT *
FROM tableA A
WHERE NOT EXISTS
(SELECT idx FROM tableB B WHERE B.idx = A.idx)

Saya menggunakan kueri dalam Access terhadap database SQL Server.

Michael Richardson
sumber
2
Sebagai samping, pendekatan yang tampaknya identik WHERE A.idx NOT IN (...) adalah tidak identik karena perilaku trivalen dari NULL(yaitu NULLtidak sama dengan NULL(atau tidak sama), oleh karena itu jika Anda memiliki salah NULL di tableBAnda akan mendapatkan hasil yang tak terduga!)
Elaskanator

Jawaban:

58

Perbedaan terbesar adalah tidak di gabung vs tidak ada, itu (seperti yang tertulis), itu SELECT *.

Pada contoh pertama, Anda mendapatkan semua kolom dari keduanya A dan B, sedangkan dalam contoh kedua, Anda hanya mendapatkan kolom dari A.

Dalam SQL Server, varian kedua sedikit lebih cepat dalam contoh yang dibuat sangat sederhana:

Buat dua tabel sampel:

CREATE TABLE dbo.A
(
    A_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);

CREATE TABLE dbo.B
(
    B_ID INT NOT NULL
        PRIMARY KEY CLUSTERED
        IDENTITY(1,1)
);
GO

Masukkan 10.000 baris ke setiap tabel:

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

Hapus setiap baris ke-5 dari tabel kedua:

DELETE 
FROM dbo.B 
WHERE B_ID % 5 = 1;

SELECT COUNT(*) -- shows 10,000
FROM dbo.A;

SELECT COUNT(*) -- shows  8,000
FROM dbo.B;

Lakukan dua SELECTvarian pernyataan tes :

SELECT *
FROM dbo.A
    LEFT JOIN dbo.B ON A.A_ID = B.B_ID
WHERE B.B_ID IS NULL;

SELECT *
FROM dbo.A
WHERE NOT EXISTS (SELECT 1
    FROM dbo.B
    WHERE b.B_ID = a.A_ID);

Rencana eksekusi:

masukkan deskripsi gambar di sini

Varian kedua tidak perlu melakukan operasi filter karena dapat menggunakan operator anti-semi join kiri.

Max Vernon
sumber
23

Secara logis mereka identik, tetapi NOT EXISTSlebih dekat dengan AntiSemiJoin yang Anda minta, dan umumnya lebih disukai. Ini juga menyoroti lebih baik bahwa Anda tidak dapat mengakses kolom di B, karena itu hanya digunakan sebagai filter (sebagai lawan tersedia dengan nilai NULL).

Bertahun-tahun yang lalu (SQL Server 6.0 ish), LEFT JOINlebih cepat, tetapi itu tidak terjadi untuk waktu yang lama. Hari-hari ini, NOT EXISTSsedikit lebih cepat.


Dampak terbesar dalam Access adalah bahwa JOINmetode ini harus menyelesaikan gabungan sebelum memfilternya, membangun kumpulan yang bergabung dalam memori. Menggunakannya NOT EXISTSmemeriksa baris tetapi tidak mengalokasikan ruang untuk kolom. Plus, ia berhenti mencari begitu menemukan baris. Kinerja bervariasi sedikit lebih banyak di Access, tetapi aturan umum yang NOT EXISTScenderung sedikit lebih cepat. Saya akan cenderung mengatakan itu "praktik terbaik", karena ada lebih banyak faktor yang terlibat.

Rob Farley
sumber
6

Pengecualian yang saya perhatikan sebagai NOT EXISTSyang lebih unggul (namun sedikit) LEFT JOIN ... WHERE IS NULLadalah ketika menggunakan Server Tertaut .

Dari memeriksa rencana eksekusi, tampak bahwa NOT EXISTSoperator dieksekusi dalam mode loop bersarang. Dimana dijalankan pada basis per baris (yang saya kira masuk akal).

Contoh rencana eksekusi yang menunjukkan perilaku ini: masukkan deskripsi gambar di sini

robopim
sumber
1
Server yang terhubung sangat brutal untuk hal semacam itu. Suatu pendekatan yang mungkin untuk menyelesaikan masalah itu adalah dengan menyalin data jarak jauh melalui tautan server tertaut menggunakan sederhana INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=ykemudian menjalankan NOT EXISTS (...)klausa terhadap salinan sementara dari database.
Max Vernon
2
Sedikit malu-malu sekarang untuk mendapatkan respons dari Max Vernon pada posting saya! Mengesampingkan Fanboy. Lucu Anda menyebutkan itu, karena saya telah menggunakan pendekatan yang tepat pada beberapa kesempatan untuk mendapatkan hasil maksimal dari situasi lintas-server.
robopim
1
Cheers, @pimbrouwers - terima kasih atas komentar baik Anda!
Max Vernon
5

Secara umum, mesin akan membuat rencana eksekusi berdasarkan dasarnya pada:

  1. Jumlah baris dalam A dan B
  2. Apakah ada Indeks pada A dan / atau B.
  3. Jumlah baris hasil yang diharapkan (dan baris perantara)
  4. Bentuk permintaan input (yaitu pertanyaan Anda)

Untuk (4):

Rencana "tidak ada" mendorong rencana pencarian berdasarkan pada tabel B. Ini adalah pilihan yang baik ketika tabel A kecil dan tabel B besar (dan indeks ada di B).

Rencana "antijoin" adalah pilihan yang baik ketika tabel A sangat besar atau tabel B sangat kecil atau tidak ada indeks pada B dan mengembalikan set hasil yang besar.

Namun itu hanya "dorongan", seperti input tertimbang. A (1), (2), (3) yang kuat sering membuat pilihan untuk (4) diperdebatkan.

(Mengabaikan efek dari contoh Anda mengembalikan kolom yang berbeda karena *, dialamatkan oleh jawaban @MaxVernon.).

crokusek
sumber