LEFT JOIN veya NOT EXISTS kullanımı arasında en iyi yöntem


67

LEFT JOIN veya NOT EXISTS formatını kullanmak arasında en iyi yöntem var mı?

Birini diğerinden kullanmanın faydası nedir?

Eğer değilse, hangisi tercih edilmelidir?

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)

Bir SQL Server veritabanına karşı Access içinde sorgular kullanıyorum.


2
Bir kenara, görünüşte birbirinin aynı yaklaşım WHERE A.idx NOT IN (...) olduğunu değil aynı dolayı üç değerlikli davranışına NULL(yani NULLeşit değildir NULLSahip nedenle eğer) (ne de eşitsiz herhangi NULL içinde tableBsize beklenmedik sonuçlar alırsınız!)
Elaskanator

Yanıtlar:


58

En büyük fark birleşme içinde değil, var olmadığıdır (olduğu gibi) SELECT *.

İlk örnekte, sen tüm sütunları almak hem A ve Bikinci örnekte, sadece sütunları olsun oysa A.

SQL Server'da, ikinci değişken çok basit bir bağlamda verilen örnekte biraz daha hızlıdır:

İki örnek tablo oluşturun:

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

Her tabloya 10.000 satır ekleyin:

INSERT INTO dbo.A DEFAULT VALUES;
GO 10000

INSERT INTO dbo.B DEFAULT VALUES;
GO 10000

Her 5. sırayı ikinci tablodan çıkarın:

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;

İki test SELECTifadesi değişkenini gerçekleştirin:

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);

İcra planları:

görüntü tanımını buraya girin

İkinci varyantın sol anti-yarı birleştirme operatörünü kullanabilmesi nedeniyle filtre işlemini gerçekleştirmesi gerekmez.


23

Mantıksal olarak bunlar özdeştirler, ancak NOT EXISTSistediğiniz AntiSemiJoin'e daha yakındır ve genellikle tercih edilir. Ayrıca, B'deki sütunlara erişemediğinizden daha iyi vurgu yapar, çünkü yalnızca bir filtre olarak kullanılır (NULL değerleri ile kullanılabilir olmalarının aksine).

Yıllar önce (SQL Server 6.0 ish), LEFT JOINdaha hızlıydı, ancak bu çok uzun zamandır böyle değildi. Bu günlerde, NOT EXISTSmarjinal olarak daha hızlı.


Access'teki en büyük etki, JOINyöntemin birleştirilmiş kümeyi bellekte oluşturarak, filtrelemeden önce birleşimi tamamlaması gerektiğidir. NOT EXISTSBunu kullanmak , satırı kontrol eder ancak sütunlara yer ayırmaz. Ayrıca, bir satır bulduğunda aramayı durdurur. Access'te performans biraz daha değişiyor, ancak genel bir kural, NOT EXISTSbiraz daha hızlı olma eğiliminde. Daha fazla faktör olduğu için bunun "en iyi uygulama" olduğunu söylemeye daha az yatkın olurdum.


6

Bağlantılı Sunucular kullanılırken NOT EXISTS(ancak marjinal olarak) üstün olduğuna dikkatimi çeken bir istisna .LEFT JOIN ... WHERE IS NULL

Yürütme planlarını inceledikten sonra, NOT EXISTSoperatörün iç içe geçmiş bir döngü biçiminde yürütüldüğü anlaşılıyor . Bu nedenle satır başına gerçekleştirilir (sanırım mantıklı).

Bu davranışı gösteren örnek yürütme planı: görüntü tanımını buraya girin


1
Bağlantılı sunucular bu tür şeyler için acımasızdır. Bu sorunu çözmenin olası bir yolu, uzaktaki verileri bağlantılı bir sunucu bağlantısı üzerinden kopyalamak ve INSERT INTO #t (a,b,c) SELECT a,b,c FROM LinkedServer.database.dbo.table WHERE x=ybu NOT EXISTS (...)maddeyi geçici veritabanının geçici kopyasına karşı çalıştırmaktır .
Max Vernon,

2
Benim görevimde Max Vernon'dan bir yanıt almak için şu anda biraz korkak! Fanboy bir yana. Söylemelisin ki, bu çapraz yaklaşımı birkaç kez bu çapraz sunucu durumlarından en iyi şekilde yararlanmak için kullandım.
robopim,

1
Şerefe, @pimbrouwers - Nazik yorumunuz için teşekkürler!
Max Vernon,

5

Genel olarak, motor esas olarak aşağıdakilere dayanan bir uygulama planı oluşturacaktır:

  1. A ve B'deki satır sayısı
  2. A ve / veya B'de bir Endeks olup olmadığı
  3. Beklenen sonuç satırı sayısı (ve ara sıralar)
  4. Giriş sorgusunun formu (yani sorunuz)

4 için):

"Var değil" planı, B tablosunda arama temelli bir planı teşvik eder. Bu, A tablosunun küçük ve B tablosunun büyük olması (ve B'de bir indeks bulunması) için iyi bir seçimdir.

"Birleşme önleme" planı, A tablosu çok büyük olduğunda veya B tablosu çok küçük olduğunda veya B üzerinde indeks olmadığında ve büyük bir sonuç kümesi döndürdüğünde iyi bir seçimdir.

Ancak, ağırlıklı bir girdi gibi, sadece bir "teşvik" dir. Güçlü (1), (2), (3), sıklıkla (4) tartışma için seçim yapar.

(Örneğin, @MaxVernon cevapları tarafından ele alınan * nedeniyle farklı sütunlar döndürme etkisinin yoksayılması.).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.