JOIN veya WHERE içindeki durum


194

JOIN yan tümcesinde WHERE yan tümcesine koşul koymak arasında herhangi bir fark (performans, en iyi uygulama vb.) Var mı?

Örneğin...

-- Condition in JOIN
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND CUS.FirstName = 'John'

-- Condition in WHERE
SELECT *
FROM dbo.Customers AS CUS
INNER JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE CUS.FirstName = 'John'

Hangisini tercih edersiniz (ve belki neden)?


4
İki sorguyu çalıştırdınız mı? İki sorgu tarafından oluşturulan yürütme planlarını kontrol ettiniz mi? Ne gözlemledin?
S.Lott

22
@ S.Lott, bu sorgu yalnızca örnek amaçlıdır. Ben sadece - genel olarak - tercih edilen yöntem - eğer merak ediyorum.
Steve Dignan

1
@Steve Dignan: Bunu örnek verilerle karşılaştırmalı ve sorgu planlarına bakmalısınız. Cevap çok, çok açık olacak. Ve - bonus - daha karmaşık durumlar ortaya çıktığında tekrar kullanabileceğiniz bir kod parçasına sahip olacaksınız.
S.Lott

1
Durum ilişkiyi açıklarsa şahsen JOIN deyimine koyarım. Sonuç kümesini filtreleyen genel koşullar o zaman NEREDE bölümüne gider. Örn.FROM Orders JOIN OrderParties ON Orders.Id = OrderParties.Order AND OrderParties.Type = 'Recipient' WHERE Orders.Status = 'Canceled'
Glutexo

Yanıtlar:


154

İlişkisel cebir içinde Yüklem değişime imkan vermektedir WHEREmaddesi ve INNER JOINbu nedenle bile, INNER JOINsahip sorgular WHEREhükümler onlar o kadar iyileştirici tarafından rearrranged yüklemler olabilir zaten hariç olabilir sırasında JOINsürecin.

Sorguları mümkün olan en okunaklı şekilde yazmanızı öneririm.

Bazen bu, INNER JOINgöreceli olarak "eksik" WHEREhale getirmeyi ve filtreleme kriterlerinin listelerinin daha kolay korunmasını sağlamak için bazı ölçütleri basitçe koymayı içerir .

Örneğin:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
    AND c.State = 'NY'
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
    AND a.Status = 1

Yazmak:

SELECT *
FROM Customers c
INNER JOIN CustomerAccounts ca
    ON ca.CustomerID = c.CustomerID
INNER JOIN Accounts a
    ON ca.AccountID = a.AccountID
WHERE c.State = 'NY'
    AND a.Status = 1

Ama elbette buna bağlı.


7
Sadece temiz sorgu veya okunabilirlik ile ilgili değil, performansla da ilgilidir. birleştirme koşullarını düzgün bir şekilde dizine eklenmiş tablolarla büyük miktarda veri için performansı artırır.
Shahdat

1
Birkaç milyon kayıtta 5-6 tabloya katılarak aylık satış raporları çalıştırıyorum. Perf% 30
iyileşiyor

2
@Shahdat, filtre koşullarınızı nerede yan tümcesinden iç birleşime taşıyacak kadar önemli bir performans farkı elde ediyorsanız, bu yürütme planlarını yayınlamanız gerekir.
Cade Roux

4
@Cade İcra planlarını araştırdım - her iki senaryo da aynı maliyeti gösteriyor. Sorguları birden çok kez çalıştırıyorum, hem de yaklaşık aynı zaman alıyor gibi görünüyor. Daha önce, sorguları üretim üzerinde çalıştırıyordum ve veritabanı canlı kullanıcılar tarafından kullanıldığından önemli performans farkı elde ettim. Bu karışıklık için özür dilerim.
16'da Şahdat

4
Bu cevap INNER JOIN'ler için doğrudur, ancak sol / sağ birleşimler için geçerli değildir.
17'de

123

İç birleşimler için gerçekten bir fark görmedim (ancak tüm performans ayarlarında olduğu gibi, koşullarınız altında veritabanınızı kontrol etmeniz gerekir).

Ancak, sol veya sağ birleşimleri kullanıyorsanız, durumu koyduğunuz yerde büyük bir fark yaratır. Örneğin bu iki sorguyu düşünün:

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderDate >'20090515'

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
AND ORD.OrderDate >'20090515'

Birincisi, sadece 15 Mayıs 2009 tarihinden sonra bir emri olan kayıtları verir, böylece sol birleşimi bir iç birleşime dönüştürür.

İkincisi bu kayıtları artı siparişi olmayan müşterileri verecektir. Sonuç kümesi, koşulu nereye koyduğunuza bağlı olarak çok farklıdır. (Select * yalnızca örnek amaçlıdır, elbette bunu üretim kodunda kullanmamalısınız.)

Bunun tek istisnası, yalnızca bir tablodaki kayıtları görmek, diğerini görmek istemenizdir. Sonra birleştirme değil koşul için where yan tümcesini kullanın.

SELECT *
FROM dbo.Customers AS CUS 
LEFT JOIN dbo.Orders AS ORD 
ON CUS.CustomerID = ORD.CustomerID
WHERE ORD.OrderID is null

Örneklerle açıkladığınız için teşekkür ederiz
Rennish Joseph

1
"böylece sol birleşimi bir iç birleşime çevirir". Nasıl? Biraz detaylandırabilir misin?
user1451111

@ user1451111 SOL / SAĞ BİRLEŞTİRMENİN ne döndürdüğünü öğrenin: INNER JOIN satırları artı NULL'lar tarafından genişletilen benzersiz sol / sağ tablo satırları. FULL JOIN, NULL'lar tarafından genişletilen UNION ALL eşleşmeyen sol ve sağ tablo satırlarını içeren INNER JOIN satırlarını döndürür. Her zaman bir DIER JOIN'in parçası olarak INNER JOIN'a ne istediğinizi bilin. Bir OUTER JOIN ON, NULL tarafından genişletilen tüm satırları kaldırdıktan sonra NULL genişletilmiş bir sütunun NULL olmamasını gerektiren bir WHERE veya ON, yani yalnızca INNER JOIN satırları bırakır, yani "OUTER JOIN'i INNER JOIN'a dönüştürür".
philipxy

1
@ user1451111 veya daha basit terimlerle: A left join BA'dan her satır, B'den eşleşen her satıra birleştirilir. B'nin eşleşen bir satırı yoksa, A sütunlarının bir değeri vardır, ancak bu satırdaki B'deki her sütun NULL değerleri olarak gösterilir. Eğer where B.somecolumn = ‘somevalue’yazdıysanız, 'somevalue' ile karşılaştırılan bir NULL (B.somecolumn) var. NULL ile karşılaştırıldığında her şey yanlıştır, bu nedenle A satırı için eşleşen B satırının olmadığı tüm satırlarınız elenir ve elde ettiğiniz sonuçlar bir INNER JOIN'in verdiği ile aynıdır, bu nedenle dış birleşim bir iç olan haline gelmiştir.
Caius Jard

evet Ben sonuçları için aynı kontrol ettik: SELECT funds.id, prospects.id FROM fundsiç birleşim umutları (prospects.id = funds.lead_id ve prospects.is_manual = 'hayır') ve SELECT funds.id, prospects.id FROM fundssoldan prospects.is_manual = 'no'
Dhect Dhiman

25

Çoğu RDBMS ürünü her iki sorguyu da aynı şekilde optimize eder. Peter Gulutzan ve Trudy Pelzer'in "SQL Performans Ayarı" nda, birçok RDBMS markasını test ettiler ve hiçbir performans farkı bulamadılar.

Birleştirme koşullarını sorgu kısıtlama koşullarından ayrı tutmayı tercih ederim.

Eğer kullanıyorsanız OUTER JOINbazen maddesini katılmak koşulları koymak için gereklidir.


1
Sözdizimsel olarak daha temiz olduğuna katılıyorum ve bu kitap hakkındaki bilginize ve çok yüksek itibarınıza ertelemeliyim, ancak geçen hafta çok farklı yürütme planları, CPU süreleri ve mantıksal okumalarla 4 sorgu düşünebilirim Tahminlerin birleşme yerine taşındım.
marr75

2
En iyi uygulamaları soruyordun. Belirli bir RDBMS uygulamasının nasıl çalıştığını test etmeye başlar başlamaz, diğer insanlar doğru tavsiyelerde bulundu: kıyaslama.
Bill Karwin

12

JOIN gerçekleştikten sonra NEREYE filtre uygulanacak.

JOIN işlemi sırasında satırların eklenmesini önlemek için JOIN'e filtre uygulayın.


10
Anlamsal olarak, INNER JOIN işlemi sırasında önlenirler, ancak optimizer INNER JOIN ve WHERE irade tahminlerini yeniden düzenleyebilir, bu nedenle optimizer isterse daha sonra bunları hariç tutmakta serbesttir.
Cade Roux

1
Cade Roux: Doğru. Genellikle SQL'de yazdıklarınız, her şey söylendiğinde ve yapıldığında optimize edicinin size vereceği şey değildir. O zaman bunun tüm teori dünyasında doğru olacağını varsayalım, cevabınız otomatik sorgu optimizatörleri dünyasında elbette daha doğru :)
TheTXI

Bu durumun açıklamasını seviyorumON
Robert Rocha

3

Ben JOIN tam tablolar / Görünümler katılmak için tercih ve daha sonra sonuç kümesinin yüklem tanıtmak için NEREDE kullanın.

Sözdizimsel olarak daha temiz hisseder.


2

Birleşimde filtreleme yaparken genellikle performans artışları görüyorum. Özellikle her iki tablo için dizinli sütunlara katılabiliyorsanız. Çoğu sorguyu da yaparak mantıksal okumaları azaltabilmelisiniz, yani yüksek hacimli bir ortamda, yürütme süresinden çok daha iyi bir performans göstergesi.

Birisi SQL karşılaştırmasını gösterdiğinde her zaman hafif eğlendim ve dev sunucusunda gece yarısında bir sproc'ın her iki sürümünü de 50.000 kez çalıştırdım ve ortalama süreleri karşılaştırın.


0

Birleştirmeye koşulu koymak benim için "anlamsal olarak yanlış" gibi görünüyor, çünkü JOIN'lerin "için" olduğu şey bu değil. Ama bu çok niteliksel.

Ek sorun: bir iç birleşimden, örneğin bir birleşim yerine geçmeye karar verirseniz, koşulun JOIN içinde olması beklenmedik sonuçlara yol açabilir.


3
Bazen bu sonuçlar biraz "beklenir" ve hatta bazen "kasıtlı" olur (örneğin, WHERE koşulunun JOIN koşulundan farklı anlambilime sahip olduğu dış birleşimlerde).
Marcel Toth

0

Bence daha büyük bir masanız olduğunda birleşimler daha hızlı. Özellikle daha küçük bir masa ile uğraşıyorsanız, gerçekten çok fazla bir fark değil. Birleşimler hakkında ilk öğrendiğimde, birleşimlerdeki koşulların nerede cümle koşullarının olduğu gibi olduğu ve nerede cümlenin hangi tablo üzerinde koşulun yapılması gerektiği konusunda spesifik olması durumunda bunları birbirinin yerine kullanabileceğim söylendi.


-4

Birleştirmeye koşulu eklemek daha iyidir. Performans, okunabilirlikten daha önemlidir. Büyük veri kümeleri için önemlidir.


1
Bir tür kanıtınız var mı, bahsedilen tahminlerin yerleştirilmesinin performansı nasıl etkilediğini araştırın?
Zso
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.