SQL Server 2016'da SUBSTRING () içeren tahminlerdeki değişikliklerde değişiklik var mı?


13

SUBSTRING () veya diğer dize işlevlerini içeren tahminler için SQL Server 2016'da kardinalitenin nasıl tahmin edildiğine dair değişiklikler hakkında herhangi bir belge veya araştırma var mı?

Sormamın nedeni, performansı uyumluluk modunda 130 bozulan bir sorguya bakmam ve nedeni SUBSTRING () çağrısını içeren bir WHERE yan tümcesi ile eşleşen satırların sayısındaki değişiklikle ilgili olmasıdır. Sorgu yeniden yazma ile ilgili sorunu düzelttim, ancak SQL Server 2016'da bu alandaki değişikliklerle ilgili herhangi bir belgenin farkında olup olmadığını merak ediyorum.

Demo kodu aşağıdadır. Bu test durumunda tahminler çok yakındır, ancak doğruluk verilere bağlı olarak değişir.

Test durumunda, uyumluluk seviyesi 120'de, SQL Server tahmin için histogram kullanıyor gibi görünürken, uyumluluk seviyesi 130'da SQL Server, tablo eşleşmelerinin sabit% 10'unu varsayıyor gibi görünmektedir.

CREATE DATABASE MyStringTestDB;
GO
USE MyStringTestDB;
GO
DROP TABLE IF EXISTS dbo.StringTest;
CREATE TABLE dbo.StringTest ( [TheString] varchar(15) );
GO
INSERT INTO dbo.StringTest
VALUES
( 'Y5_CLV' );
INSERT INTO dbo.StringTest
VALUES
( 'Y5_EG3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_NE' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_PQT' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_T2V' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_TT4' );
INSERT INTO dbo.StringTest
VALUES
( 'ZY_ZKK' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_LW6' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_QO3' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_TZ7' );
INSERT INTO dbo.StringTest
VALUES
( 'ZZ_UZZ' );

CREATE CLUSTERED INDEX IX_Clustered ON dbo.StringTest (TheString);

/* 
Uses fixed % for estimate; 1.1 rows estimated in this case.
    Plan for computation:
        CSelCalcFixedFilter (0.1) <----
            Selectivity: 0.1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 130;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/* 
Uses histogram to get estimate of 1
 CSelCalcPointPredsFreqBased <----
      Distinct value calculation:
          CDVCPlanLeaf
              0 Multi-Column Stats, 1 Single-Column Stats, 0 Guesses
      Individual selectivity calculations:
          (none)
    Loaded histogram for column QCOL: [DBA].[dbo].[StringTest].TheString from stats with id 1
*/
ALTER DATABASE MyStringTestDB SET compatibility_level = 120;
GO
SELECT * 
FROM dbo.StringTest 
WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);

/*
-- Simpler rewrite; works fine in both compat levels and gets better estimate.
SELECT * 
FROM dbo.StringTest 
WHERE TheString LIKE 'ZZ[_]%'
OPTION (QUERYTRACEON 2363, QUERYTRACEON 3604);
*/

1
Belirli bir soru hakkında emin değilim, ancak Y5_EG3dizeler sadece kodlar ve her zaman büyük harfse, her zaman bir ikili harmanlama belirtmeyi deneyebilirsiniz - Latin1_General_100_BIN2- filtreleme işlemlerinde hızı geliştirmelidir. Sadece eklemek COLLATE Latin1_General_100_BIN2için CREATE TABLEhemen sonra açıklamada varchar(15). Plan üretimini / tahminini de etkileyip etkilemediğini merak ediyorum.
Solomon Rutzky

Yanıtlar:


8

Hiçbir belgenin farkında değilim. Buna baktım ve yorum yapmak için çok uzun olan bazı gözlemler yaptım.

% 10 tahmini her zaman bir bozulma değildir. Aşağıdaki örneği ele alalım.

TRUNCATE TABLE dbo.StringTest

INSERT INTO dbo.StringTest
SELECT TOP (1000000) 'ZZ_' + LEFT(NEWID(), 12)
FROM   master..spt_values v1,
       master..spt_values v2;

ve WHEREsorunuzdaki madde.

WHERE SUBSTRING(TheString, 1, CHARINDEX('_',TheString) - 1) = 'ZZ'

Tabloda bir milyon satır var. Hepsi yüklemle eşleşir. Uyum düzeyi 130 altında,% 10 tahmin 100.000'lik bir tahmin verir. 120'nin altında tahmini satırlar 1.03913'tür.

120 davranışı histogramı kullanır, ancak yalnızca farklı satırların sayısını elde etmek için kullanılır. Benim durumumdaki yoğunluk vektörü 1.039131E-06 gösteriyor ve tahmini satır sayısını elde etmek için bu tablo kardinalitesi ile çarpılıyor. Tüm değerler aslında farklıdır, ancak hepsi yüklemle eşleşir.

query_optimizer_estimate_cardinalityGenişletilmiş etkinliğin izlenmesi, 130'un altında iki farklı <StatsCollection Name="CStCollFilter"etkinlik olduğunu gösterir. İlki 100.000'i tahmin ediyor. İkincisi histogramı yükler ve 1.04 tahminini almak için CSelCalcPointPredsFreqBased / DistinctCountCalculator'ı kullanır. Bu ikinci sonuç kullanılmamış olarak görünür.

Gözlemlediğiniz davranış 130'da tutarlı bir şekilde uygulanmaz. ORDER BY TheString120'nin bir satır için bellek yardımı ile mücadele ettiği için bunun 130 tahmincisi için açık bir kazanç olmasını bekledim , ancak bu küçük değişiklik tahmini satırları aşağı çekmek için yeterliydi 130 durumda da 1.03913.

Ekleme OPTION (QUERYRULEOFF SelectToFilter), sıralamaya giren tahmini 100.000'e geri döndürür, ancak bellek hibesi artmaz ve sıralamadan çıkan tahminler hala tablodan ayrı değerlere dayanır.

resim açıklamasını buraya girin

Benzer şekilde, paralellik için maliyet eşiğini ayarlamak, böylece sorgunun paralel bir plana ulaşması 130 vakada daha düşük tahmine geri dönmek için yeterli olmuştur. Ekleme QUERYTRACEON 8757de daha düşük bir tahmine neden olur. % 10'luk tahmin yalnızca önemsiz planlar için korunuyor gibi görünüyor.

İle önerdiğiniz yeniden yazma

WHERE TheString LIKE 'ZZ[_]%'

Her ikisine de çok daha üstün tahminler gösterir. Bunun çıktısı

  CSelCalcTrieBased

      Column: QCOL: [MyStringTestDB].[dbo].[StringTest].TheString

Kullanıldığını gösteren çalışır . Bu konuda diğer bilgiler sadece yukarıdaki dize özet istatistikler bölümünde ise burada .

Ancak orijinal sorgunuzla aynı değildir. İlk örneğinin _artık dinamik olarak bulunmak yerine her zaman üçüncü karakter olduğu varsayılmaktadır.

Bu varsayım orijinal sorgunuza sabit kodlanmışsa

 WHERE SUBSTRING(TheString, 1, 3) = 'ZZ_'

Tahmin yöntemi olarak değişir CSelCalcHistogramComparison(INTERVAL)ve tahmini satırlar doğru olur.

Bunu bir aralığa dönüştürebilir

WHERE TheString >=  'ZZ_' AND TheString < ???

ve bu aralıktaki değerlere sahip satır sayısını tahmin etmek için histogramı kullanın.

Ancak bu yalnızca kardinalite tahmini için geçerlidir. LIKEçalışma zamanında aralık araması kullanabildiğinden tercih edilir. SUBSTRING(TheString, 1, 3)ya LEFT(TheString, 3)da yapamam.

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.