özet
SQL Server, doğru birleştirmeyi (iç veya dış) kullanır ve uygula ve birleştir arasında iç çeviriler gerçekleştirirken, özgün sorgunun tüm anlamlarını onurlandırmak için gerektiğinde projeksiyonlar ekler .
Planlardaki farklılıkların tümü , SQL Server'da bir group by cümlesi olan ve olmayan agregaların farklı semantiği ile açıklanabilir .
ayrıntılar
Katıl ve Uygula
Bir başvuru ve bir katılma arasında ayrım yapabilmemiz gerekir :
Uygulamak
İç (alt) giriş uygulamak mevcut dış sıra tarafından sağlanan bir veya daha fazla iç yan parametre değerleri ile, dış (alt), girişin her satır için çalıştırılır. Genel sonucu uygulanır parametreli iç yan infaz tarafından üretilen tüm satırları kombinasyonu (her birlik) 'dir. Parametreler araçlarının varlığı geçerlidir bazen katılmak korelasyon olarak anılır.
Bir uygula hep tarafından icra planlarında uygulanan İç içe döngü operatör. İşleç, birleşim tahminlerinden ziyade bir Dış Referanslar özelliğine sahip olacaktır . Dış referanslar, halkanın her bir yinelemesinde dış taraftan iç tarafa geçirilen parametrelerdir.
Katılmak
Birleştirme, birleştirme işlecindeki birleştirme yüklemesini değerlendirir. Birleştirme genellikle SQL Server'daki Hash Match , Merge veya Nested Loops operatörleri tarafından uygulanabilir .
Tüm İç içe döngü seçilmiş bir mesafede, bu ayırt edilebilir geçerli olmaması ile dış Kaynaklar (ve genellikle varlığı yüklemi birleştirme). Bir iç giriş katılması dış girişten hiç referanslar değerleri - iç taraf hala her dış satır için bir kez gerçekleştirilir, ancak iç yan infaz mevcut dış sıra herhangi bir değerlerine bağlı değildir.
Daha fazla ayrıntı için gönderime bakın Nested Loops'a karşı Uygula'ya katılın .
... neden bir var dış yerine yürütülmesi planında katılmak iç katılmak?
Dış birleştirme, optimize edici , daha ucuz bir birleştirme tabanlı plan bulup bulamayacağını görmek için bir uygulamayı bir birleşime (adlı bir kural kullanarak ApplyHandler
) dönüştürdüğünde ortaya çıkar . Bir dış olmak için katılmak için gereklidir katılmak doğruluğu zaman geçerli bir içerdiği sayıl agrega . Bir iç birleşimin , orijinalin uygulayacağımızla aynı sonuçları üreteceği garanti edilmez .
Skaler ve Vektör Kümeleri
- İlgili
GROUP BY
cümlesine sahip olmayan bir toplama , skaler bir toplamadır.
- Karşılık gelen bir cümleye sahip bir toplama
GROUP BY
, bir vektör toplamadır.
SQL Server'da, bir ölçekleme toplamı, birleştirilecek satır verilmemiş olsa bile, her zaman bir satır oluşturur. Örneğin, COUNT
satır içermeyen skaler agrega sıfırdır. Satır içermeyen bir vektör COUNT
toplamı boş kümedir (hiç satır yok).
Aşağıdaki oyuncak sorguları farkı göstermektedir. Skaler ve Vektör Agregaları ile Eğlenceli makalemde skaler ve vektör agregaları hakkında daha fazla bilgi bulabilirsiniz .
-- Produces a single zero value
SELECT COUNT_BIG(*) FROM #MyTable AS MT WHERE 0 = 1;
-- Produces no rows
SELECT COUNT_BIG(*) FROM #MyTable AS MT WHERE 0 = 1 GROUP BY ();
db <> keman demosu
Dönüştürme katılmak için geçerlidir
Daha önce , orijinal uygulama bir skaler agrega içerdiğinde , birleşmenin doğruluk için bir dış birleşim olması gerektiğini belirtmiştim . Bu durumun neden ayrıntılı olduğunu göstermek için, soru sorgusunun basitleştirilmiş bir örneğini kullanacağım:
DECLARE @A table (A integer NULL, B integer NULL);
DECLARE @B table (A integer NULL, B integer NULL);
INSERT @A (A, B) VALUES (1, 1);
INSERT @B (A, B) VALUES (2, 2);
SELECT * FROM @A AS A
CROSS APPLY (SELECT c = COUNT_BIG(*) FROM @B AS B WHERE B.A = A.A) AS CA;
Sütun için doğru sonuç c
ise sıfırdır , çünkü COUNT_BIG
bir olduğunu sayıl agrega. Bu uygulama sorgusunu birleştirme formuna çevirirken SQL Server, T-SQL'de ifade edilirse aşağıdakine benzer bir iç alternatif oluşturur:
SELECT A.*, c = COALESCE(J1.c, 0)
FROM @A AS A
LEFT JOIN
(
SELECT B.A, c = COUNT_BIG(*)
FROM @B AS B
GROUP BY B.A
) AS J1
ON J1.A = A.A;
Uygulamayı ilişkisiz bir birleşim olarak yeniden yazmak için GROUP BY
, türetilmiş tabloya bir tane eklememiz gerekir (aksi takdirde A
birleştirilecek sütun olamaz ). Birleşim bir dış birleşim olmalıdır, böylece tablodaki her satır @A
çıktıda bir satır üretmeye devam eder. Birleştirme yüklemesi true olarak değerlendirilmediğinde, sol birleştirme bir NULL
for sütunu üretecektir c
. Yani NULL
ihtiyaçları tarafından sıfıra Çevrilecek COALESCE
bir doğru dönüşümü tamamlamak için geçerlidir .
Aşağıdaki demo, hem dış birleştirmenin hem de orijinal uygulama sorgusuyla birleştirmeCOALESCE
kullanarak aynı sonuçları üretmenin gerekli olduğunu gösterir :
db <> keman demosu
İle GROUP BY
... grubu maddeye göre uncomment etmek neden içsel bir birleşmeyle sonuçlanır?
Basitleştirilmiş örneğe devam etmek, ancak aşağıdakileri eklemek GROUP BY
:
DECLARE @A table (A integer NULL, B integer NULL);
DECLARE @B table (A integer NULL, B integer NULL);
INSERT @A (A, B) VALUES (1, 1);
INSERT @B (A, B) VALUES (2, 2);
-- Original
SELECT * FROM @A AS A
CROSS APPLY
(SELECT c = COUNT_BIG(*) FROM @B AS B WHERE B.A = A.A GROUP BY B.A) AS CA;
COUNT_BIG
Şimdi ise vektör boş giriş kümesi için doğru sonuç artık sıfır olduğu öyledir, agrega hiç hiçbir satır . Başka bir deyişle, yukarıdaki ifadeleri çalıştırmak çıktı üretmez.
Dan çeviri yaparken bu semantik onuruna çok daha kolaydır uygulamak için katılmak beri, CROSS APPLY
doğal olarak hiçbir iç yan satırları oluşturan her türlü dış sırayı reddeder. Bu nedenle, artık ekstra ifade projeksiyonu olmadan artık bir iç birleşimi güvenle kullanabiliriz:
-- Rewrite
SELECT A.*, J1.c
FROM @A AS A
JOIN
(
SELECT B.A, c = COUNT_BIG(*)
FROM @B AS B
GROUP BY B.A
) AS J1
ON J1.A = A.A;
Aşağıdaki demo, iç birleşim yeniden yazmasının, orijinalin vektör toplamıyla uygulandığıyla aynı sonuçları verdiğini göstermektedir:
db <> keman demosu
Optimize edici, hızlı bir şekilde ucuz bir birleştirme planı bulduğu için (yeterince iyi plan bulundu) küçük tablo ile birleştirme iç birleşimi seçer . Maliyete dayalı iyileştirici, bir döngüye birleştirme veya yeniden öngörü ipucu kullanıldığında burada olacağı için, birleştirmeyi yeniden bir uygulamaya yeniden yazmaya devam edebilir - belki de daha ucuz bir uygulama planı bulmak - ancak bu durumda çabaya değmez.
notlar
Basitleştirilmiş örnekler, anlamsal farklılıkları daha net göstermek için farklı içeriklere sahip farklı tablolar kullanır.
Optimize edicinin, kendiliğinden birleştirme ile eşleşmeyen (birleşmeyan) satırlar üretememe konusunda mantık yürütmesi gerektiği iddia edilebilir, ancak bugün bu mantığı içermez. Aynı tabloya bir sorguda birden çok kez erişmenin, yalıtım düzeyine ve eşzamanlı etkinliğe bağlı olarak genel olarak aynı sonuçları üreteceği garanti edilmez.
Optimize edici bu anlambilim ve uç durumlar için endişe duymaktadır, bu yüzden yapmanız gerekmez.
Bonus: İç Başvuru Planı
SQL Server edebilir bir iç üretmek uygulamak planı (bir iç değil katılmak sadece maliyet nedenleriyle değil seçerse, örneğin sorgu için bir plan!). Soruda gösterilen dış birleştirme planının maliyeti, dizüstü bilgisayarımın SQL Server 2017 örneğinde 0.02898 birimdir.
Belgelendirilmemiş ve desteklenmeyen izleme bayrağı 9114'ü (devre dışı bırakan vb.) Kullanarak yalnızca örnekleme amacıyla bir uygulama (ilişkilendirilmiş birleştirme) planı uygulayabilirsinizApplyHandler
:
SELECT *
FROM #MyTable AS mt
CROSS APPLY
(
SELECT COUNT_BIG(DISTINCT mt2.Col_B) AS dc
FROM #MyTable AS mt2
WHERE mt2.Col_A = mt.Col_A
--GROUP BY mt2.Col_A
) AS ca
OPTION (QUERYTRACEON 9114);
Bu, tembel bir dizin makarası ile bir iç içe döngü uygula planı oluşturur. Toplam tahmini maliyet 0.0463983'tür (seçilen plandan daha yüksek):
İç içe döngüler uygulama kullanan yürütme planının , GROUP BY
cümlenin varlığına bakılmaksızın "iç birleşim" semantiği kullanılarak doğru sonuçlar verdiğini unutmayın .
Gerçek dünyada, genellikle SQL Server'ın bu seçeneği doğal olarak seçmesini teşvik etmek için başvurunun iç tarafında bir aramayı destekleyecek bir dizinimiz olurdu , örneğin:
CREATE INDEX i ON #MyTable (Col_A, Col_B);
db <> keman demosu