Plan, bir SQL Server 2008 R2 RTM örneğinde derlendi (yapı 10.50.1600). Sen yüklemeniz gerekir Service Pack 3 (güncel) en son yapı 10.50.6542 e kadar getirmek için en son yamaları takip (build 10.50.6000). Bu, güvenlik, hata düzeltmeleri ve yeni özellikler dahil olmak üzere birçok nedenden dolayı önemlidir.
Parametre Gömme Optimizasyonu
Bu soruyla ilgili olarak, SQL Server 2008 R2 RTM için Parametre Gömme Optimizasyonu'nu (PEO) desteklemedi OPTION (RECOMPILE). Şu anda, ana avantajlardan birini gerçekleştirmeden yeniden derleme maliyetini ödüyorsunuz.
PEO kullanılabilir olduğunda, SQL Server yerel değişkenlerde ve parametrelerde depolanan değişmez değerleri doğrudan sorgu planında kullanabilir. Bu çarpıcı basitleştirmelere ve performans artışlarına yol açabilir. Bu konuda daha fazla bilgi var, Parametre Koklama, Gömme ve TAVSİYE Seçenekleri .
Dökülmeleri Karıştır, Sırala ve Takas Et
Bunlar yalnızca sorgu SQL Server 2012 veya sonraki sürümlerde derlendiğinde yürütme planlarında görüntülenir. Önceki sürümlerde, sorgu Profiler veya Genişletilmiş Etkinlikler kullanılarak yürütülürken dökülmeleri izlemek zorundaydık. Dökülmeler her zaman , özellikle dökülme büyükse veya G / Ç yolu basınç altındaysa, önemli performans sonuçlarına neden olabilecek kalıcı depolama destek tempdb'ye (ve buradan) fiziksel G / Ç ile sonuçlanır.
Yürütme planınızda iki Hash Match (Aggregate) operatörü var. Karma tablosu için ayrılan bellek , çıktı satırlarının tahminine dayanır (başka bir deyişle, çalışma zamanında bulunan grup sayısı ile orantılıdır). Verilen bellek, yürütme başlamadan hemen önce sabitlenir ve örneğin ne kadar boş hafızaya sahip olduğuna bakılmaksızın yürütme sırasında büyüyemez. Sağlanan planda, her iki Hash Match (Aggregate) operatörü, optimize ediciden beklenenden daha fazla satır üretir ve bu nedenle çalışma zamanında tempdb'ye dökülme yaşayabilir .
Planda ayrıca bir Hash Match (Inner Join) operatörü var. Karma tablosu için ayrılan bellek , prob tarafı giriş satırlarının tahminine dayanır . Prob girişi 847.399 satırı tahmin eder, ancak çalışma zamanında 1.223.636 ile karşılaşılır. Bu fazlalık ayrıca karma dökülmesine neden olabilir.
Gereksiz Toplam
Düğüm 8'deki Karma Eşleşme (Toplama) üzerinde bir gruplama işlemi gerçekleştirir (Assortment_Id, CustomAttrID), ancak giriş satırları çıkış satırlarına eşittir:

Bu, sütun kombinasyonunun bir anahtar olduğunu gösterir (bu nedenle gruplandırma anlamsal olarak gereksizdir). Gereksiz toplamı gerçekleştirmenin maliyeti, 1,4 milyon satırı karma bölme borsalarında (her iki taraftaki Paralellik operatörleri) iki kez geçirme ihtiyacı ile artar.
İlgili sütunların farklı tablolardan geldiği göz önüne alındığında, bu benzersizlik bilgisini optimize ediciye iletmek normalden daha zordur, böylece gereksiz gruplama işleminden ve gereksiz değişimlerden kaçınabilir.
Verimsiz iplik dağılımı
Joe Obbish'in cevabında belirtildiği gibi , 14 nolu düğümdeki değişim, satırları iş parçacıkları arasında dağıtmak için karma bölümlemeyi kullanır. Ne yazık ki, az sayıda satır ve mevcut zamanlayıcılar, üç satırın da tek bir iş parçacığında sonlandığı anlamına gelir. Görünüşte paralel plan, düğüm 9'daki değişime kadar seri olarak (paralel tepegöz ile) çalışır.
Düğüm 13'teki Farklı Sıralama'yı ortadan kaldırarak bunu (yuvarlak döngü veya yayın bölümlemesi elde etmek için) ele alabilirsiniz. Bunu yapmanın en kolay yolu, #temptablo üzerinde kümelenmiş bir birincil anahtar oluşturmak ve tabloyu yüklerken farklı işlemi gerçekleştirmektir:
CREATE TABLE #Temp
(
id integer NOT NULL PRIMARY KEY CLUSTERED
);
INSERT #Temp
(
id
)
SELECT DISTINCT
CAV.id
FROM @customAttrValIds AS CAV
WHERE
CAV.id IS NOT NULL;
Geçici tablo istatistiklerinin önbelleğe alınması
OPTION (RECOMPILE)SQL Server'ın kullanılmasına rağmen, yordam çağrıları arasında geçici tablo nesnesini ve ilişkili istatistiklerini yine de önbelleğe alabilir . Bu genellikle hoş bir performans optimizasyonudur, ancak geçici tablo bitişik yordam çağrılarında benzer miktarda veri ile doldurulursa, yeniden derlenen plan yanlış istatistiklere (bir önceki yürütmeden önbelleğe alınmış) dayalı olabilir. Bu benim makalelerde ayrıntılı olarak, Saklı Usul Geçici Tablolar ve Geçici Tablo Caching Açıklaması .
Bunu önlemek için , geçici tablo doldurulduktan sonra ve bir sorguda başvurulmadan önce OPTION (RECOMPILE)bir belirtik ile birlikte UPDATE STATISTICS #TempTablekullanın.
Sorgu yeniden yazma
Bu bölüm #Temptablonun oluşturulmasında yapılan değişikliklerin önceden yapıldığını varsayar .
Olası karma dökülme maliyetleri ve fazlalık agrega (ve çevresindeki borsalar) göz önüne alındığında, setin düğüm 10'da gerçekleştirilmesi için ödeme yapılabilir:
CREATE TABLE #Temp2
(
CustomAttrID integer NOT NULL,
Assortment_Id integer NOT NULL,
);
INSERT #Temp2
(
Assortment_Id,
CustomAttrID
)
SELECT
ACAV.Assortment_Id,
CAV.CustomAttrID
FROM #temp AS T
JOIN dbo.CustomAttributeValues AS CAV
ON CAV.Id = T.id
JOIN dbo.AssortmentCustomAttributeValues AS ACAV
ON T.id = ACAV.CustomAttributeValue_Id;
ALTER TABLE #Temp2
ADD CONSTRAINT PK_#Temp2_Assortment_Id_CustomAttrID
PRIMARY KEY CLUSTERED (Assortment_Id, CustomAttrID);
Bu PRIMARY KEY, dizin oluşturma işleminin doğru kardinalite bilgisine sahip olmasını sağlamak ve geçici tablo istatistiklerinin önbelleğe alma sorununu önlemek için ayrı bir adımda eklenir.
Bu materyalizasyon, örneğin yeterli miktarda kullanılabilir belleğe sahip olması durumunda bellekte ( tempdb I / O'dan kaçınılması) gerçekleşme olasılığı yüksektir . Bu, Eager Write davranışını iyileştiren SQL Server 2012'ye (SP1 CU10 / SP2 CU1 veya üzeri) yükselttiğinizde daha da olasıdır .
Bu eylem, iyileştiriciye ara küme doğru kardinalite bilgileri verir, istatistik oluşturmasına izin verir ve (Assortment_Id, CustomAttrID)bir anahtar olarak bildirmemize izin verir .
Popülasyon planı şu şekilde #Temp2görünmelidir (Kümelenmiş dizin taramasına dikkat edin, #TempFarklı Sıralama yok ve değişim artık yuvarlak robin satır bölümleme kullanıyor):

Bu küme kullanılabilir olduğunda, son sorgu:
SELECT
A.Id,
A.AssortmentId
FROM
(
SELECT
T.Assortment_Id
FROM #Temp2 AS T
GROUP BY
T.Assortment_Id
HAVING
COUNT_BIG(DISTINCT T.CustomAttrID) = @dist_ca_id
) AS DT
JOIN dbo.Assortments AS A
ON A.Id = DT.Assortment_Id
WHERE
A.AssortmentType = @asType
OPTION (RECOMPILE);
Manuel COUNT_BIG(DISTINCT...olarak basit bir şekilde yeniden yazabiliriz COUNT_BIG(*), ancak yeni anahtar bilgilerle optimize edici bunu bizim için yapar:

Son plan, erişimim olmayan veriler hakkındaki istatistiksel bilgilere bağlı olarak bir döngü / karma / birleştirme birleşimi kullanabilir. Bir küçük not daha: Benzer bir indeksin CREATE [UNIQUE?] NONCLUSTERED INDEX IX_ ON dbo.Assortments (AssortmentType, Id, AssortmentId);var olduğunu varsaydım .
Her neyse, nihai planlarla ilgili önemli olan şey, tahminlerin çok daha iyi olması ve karmaşık gruplama işlemlerinin tek bir Akış Toplayıcısına (bellek gerektirmeyen ve bu nedenle diske dökülemeyen) indirgenmiş olmasıdır.
Ekstra geçici tablo ile bu durumda performansın gerçekten daha iyi olacağını söylemek zor , ancak tahminler ve plan seçimleri, veri hacmi ve zaman içindeki dağılımındaki değişikliklere çok daha dayanıklı olacaktır. Bu, uzun vadede bugün küçük bir performans artışından daha değerli olabilir. Her durumda, artık nihai kararınızı dayandıracağınız çok daha fazla bilgiye sahipsiniz.
#tempYaratılışın ve kullanımın bir kazanç değil performans için bir sorun olacağını tahmin ediyorum. Dizine eklenmemiş bir tabloya yalnızca bir kez kullanılmak üzere kaydediyorsunuz. Tamamen kaldırmayı deneyin (ve muhtemelenin (select id from #temp)birexistsalt