2010-03-24 24 views
72

Bu 2 sorguyu karşılaştırın. Filtreyi birleştirme ölçütlerine veya daha önceki cümlelere koymak daha mı hızlıdır? Sonuç kümesini mümkün olan en kısa sürede azalttığından, her zaman katılma ölçütlerinde daha hızlı olduğunu hissettim, ancak kesin olarak bilmiyorum.Hangi SQL sorgusu daha hızlıdır? Katılma ölçütlerine göre filtreleme ya da nerede yan tümce?

Görmek için bazı testler yapacağım, ama aynı zamanda okumak için daha net olan fikirleri de almak istedim.

Sorgu 1

SELECT  * 
FROM  TableA a 
INNER JOIN TableXRef x 
     ON a.ID = x.TableAID 
INNER JOIN TableB b 
     ON x.TableBID = b.ID 
WHERE  a.ID = 1   /* <-- Filter here? */ 

Sorgu 2

SELECT  * 
FROM  TableA a 
INNER JOIN TableXRef x 
     ON a.ID = x.TableAID 
     AND a.ID = 1   /* <-- Or filter here? */ 
INNER JOIN TableB b 
     ON x.TableBID = b.ID 

DÜZENLEME Bazı testler yaptım ve sonuç aslında çok yakın olduğunu gösteriyor ama

WHERE yan tümcesi gerçekten kaygandır tly daha hızlı! =)

Filtrenin WHERE maddesine uygulanmasının daha mantıklı olduğuna kesinlikle katılıyorum, yalnızca performans açısından merak ettim.

KRİTERLER ELAPSED ZAMAN: 143.016 msn
ELAPSED ZAMAN KRİTERLERİ JOIN: 143.256 msn

TEST

SET NOCOUNT ON; 

DECLARE @num INT, 
     @iter INT 

SELECT @num = 1000, -- Number of records in TableA and TableB, the cross table is populated with a CROSS JOIN from A to B 
     @iter = 1000 -- Number of select iterations to perform 

DECLARE @a TABLE (
     id INT 
) 

DECLARE @b TABLE (
     id INT 
) 

DECLARE @x TABLE (
     aid INT, 
     bid INT 
) 

DECLARE @num_curr INT 
SELECT @num_curr = 1 

WHILE (@num_curr <= @num) 
BEGIN 
    INSERT @a (id) SELECT @num_curr 
    INSERT @b (id) SELECT @num_curr 

    SELECT @num_curr = @num_curr + 1 
END 

INSERT  @x (aid, bid) 
SELECT  a.id, 
      b.id 
FROM  @a a 
CROSS JOIN @b b 

/* 
    TEST 
*/ 
DECLARE @begin_where DATETIME, 
     @end_where  DATETIME, 
     @count_where INT, 
     @begin_join  DATETIME, 
     @end_join  DATETIME, 
     @count_join  INT, 
     @curr   INT, 
     @aid   INT 

DECLARE @temp TABLE (
     curr INT, 
     aid  INT, 
     bid  INT 
) 

DELETE FROM @temp 

SELECT @curr = 0, 
     @aid = 50 

SELECT @begin_where = CURRENT_TIMESTAMP 
WHILE (@curr < @iter) 
BEGIN 
    INSERT  @temp (curr, aid, bid) 
    SELECT  @curr, 
       aid, 
       bid 
    FROM  @a a 
    INNER JOIN @x x 
      ON a.id = x.aid 
    INNER JOIN @b b 
      ON x.bid = b.id 
    WHERE  a.id = @aid 

    SELECT @curr = @curr + 1 
END 
SELECT @end_where = CURRENT_TIMESTAMP 

SELECT @count_where = COUNT(1) FROM @temp 
DELETE FROM @temp 

SELECT @curr = 0 
SELECT @begin_join = CURRENT_TIMESTAMP 
WHILE (@curr < @iter) 
BEGIN 
    INSERT  @temp (curr, aid, bid) 
    SELECT  @curr, 
       aid, 
       bid 
    FROM  @a a 
    INNER JOIN @x x 
      ON a.id = x.aid 
      AND a.id = @aid 
    INNER JOIN @b b 
      ON x.bid = b.id 

    SELECT @curr = @curr + 1 
END 
SELECT @end_join = CURRENT_TIMESTAMP 

SELECT @count_join = COUNT(1) FROM @temp 
DELETE FROM @temp 

SELECT @count_where AS count_where, 
     @count_join AS count_join, 
     DATEDIFF(millisecond, @begin_where, @end_where) AS elapsed_where, 
     DATEDIFF(millisecond, @begin_join, @end_join) AS elapsed_join 
+8

Verilere bağlı olarak, WHERE ve JOIN ölçütleri farklı sonuçlar getirebilir. –

+2

@OMG Ponies çok doğru, ama çoğu kez de değil. –

+2

Farklılık olarak 5 farkı fark etmeyeceğim, fark olarak aynıdır. % 2'lik bir fark için önem kazanmak istersiniz. Sadece rastgele olmadığından emin olmak için testleri 1000 kez daha iyi çalıştırın. – TomTom

cevap

50

Performans açısından onlar bir LEFT JOIN ile INNER JOIN değiştirirseniz

Mantıken, yine anlayışı var operasyonu yapmalıdır aynıdır (ve aynı planlar üretmek). senin çok durumda

bu gibi görünecektir:

SELECT * 
FROM TableA a 
LEFT JOIN 
     TableXRef x 
ON  x.TableAID = a.ID 
     AND a.ID = 1 
LEFT JOIN 
     TableB b 
ON  x.TableBID = b.ID 

veya bu:

SELECT * 
FROM TableA a 
LEFT JOIN 
     TableXRef x 
ON  x.TableAID = a.ID 
LEFT JOIN 
     TableB b 
ON  b.id = x.TableBID 
WHERE a.id = 1 

eski sorgu 1 dışındaki a.id için herhangi bir fiili eşleşmeleri döndürmez, bu nedenle ikinci sözdizimi (WHERE ile) mantıksal olarak daha tutarlı. İki yöntem ilerledikçe,

+0

Setleri çizdiğimde, ikinci durumun neden daha tutarlı olduğunu anladım. Eski sorguda, 'a.id = 1' kısıtlaması sadece kesişme için geçerlidir, kesişme hariç sol kısımda değil. – FtheBuilder

+1

İlk örnekte 'a.id! = 1', diğerinin sadece a.id = 1 'olduğu satırlar olabilir. – FtheBuilder

+0

Diliniz belirsiz. "Mantıklı olarak, hala mantıklı olan işlemi yapmalısınız ..." ve "mantıksal olarak daha tutarlı" mantıklı değil. Yeniden ifade edebilir misiniz? – philipxy

0

Ben, ilk olarak bir daha spesifik hale getirerek tahmin verileri süz. Ancak, herhangi bir optimizasyonda olduğu gibi, should see the execution plan sizsiniz, çünkü veri büyüklüğü, sunucu donanımı vb. Üzerinde çok farklı olabilir.

0

Daha mı hızlı? Deneyin ve gör.

Okunması daha kolay? İlk benim için daha fazla "doğru" görünüyor, çünkü taşınan koşul gerçekten birleştirme ile ilgisi yok.

+0

Oh, üzgünüm adamım! – Quassnoi

2

Herhangi bir sorgu en iyileştirici ile yüzde kuruş ... onlar aynıdır.

+0

Herhangi bir gerçek iş yüküyle aynı olmadıklarından eminim. Neredeyse hiç veriye sahip değilseniz, o zaman soru değersizdir. – eKek0

+1

Gerçek iş yükü altında kontrol edin. Temel olarak - eğer aynı yürütme planını oluştururlarsa, performansları aynıdır. En azından normal/basit durumlar için (yani, 14 tabloyu birleştiren biri değil) tamamen aynı olduklarından eminim;) – TomTom

7

.Eğer benim için her zaman bir koku gibi görünüyor onları farklı kullanabilirsiniz iken

  • filtreleme içindir katılmadan tablolar
  • için/AÇIK JOIN

sonuçlanır.

Sorun olduğunda performansla uğraşın. Sonra böyle "optimisations" içine bakabilirsiniz.

0

Bu katılımın yerleştirilmesinin performans için belirleyici bir faktör olması pek olası değildir. Tsql için yürütme planlamasına aşina değilim, ancak benzer planlara otomatik olarak optimize edilecekleri olası.

0

Kural # 0: Bazı ölçütleri çalıştırın ve bakın! Hangisinin daha hızlı olacağını söylemenin tek yolu denemek. Bu tür kriterlerin SQL profiler kullanılarak gerçekleştirilmesi çok kolaydır.

Ayrıca yazılı sorgu için yürütme planı incelemek bir JOIN ve farklılıklar öne görmek için bir WHERE yan tümcesi ile. diğerleri söylediler

Son olarak, bu iki SQL Server yerleşik da dahil olmak üzere, herhangi bir iyi iyileştirici tarafından aynı şekilde muamele görmelidir.

+0

Ama sadece iç birleşimler için. Sonuç kümesi, çıkışlar için çok farklı olacaktır. – HLGEM

+0

Tabii ki. Neyse ki, sağlanan örnek iç birleştirmeler kullanır. –

+1

Talihsizce soru, birleşme ile ilgili değil, iç birleştirmelerle ilgili. – Paul

18

İç kısımlar için kriterlerinizi nereye koyduğunuz önemli değildir. SQL derleyici, filtrelemenin birleştirmenin altında gerçekleştiği bir yürütme planına dönüşür (yani, filtre ifadeleri, birleştirme koşulu gibi görünür).

Dış birleşimler farklı bir konudur, çünkü filtrenin yeri sorguların anlamını değiştirir.

+0

Böylece, iç birleşimlerde ilk önce filtreyi hesaplar ve ardından filtrenin çıkışını diğer masa ile birleştirir veya ilk önce iki tabloyu birleştirir ve ardından filtreyi uygular mı? – Ashwin

+0

@Remus Rusanu - Dış katılma durumunda semantiğin nasıl değiştiğini açıklar mısınız? Filtrenin konumuna göre farklı sonuçlar alıyorum, ancak neden bir dış birleştirmeyle – Ananth

+1

@Ananth, JOIN koşulunun eşleşmediği birleştirilen tablonun tüm sütunları için NULL'leri aldığınızı anlayamıyorum. Filtreler NULL'u karşılamayacak ve satırları ortadan kaldıracak ve OUTER katılımını INNER katılımına dönüştürecektir. –

İlgili konular