LEN () neden SQL Server 2014’te kardinalliği önemsiz kılıyor?


26

Bir dize sütunu olan bir tablo ve belirli bir uzunlukta satırları denetleyen bir yüklemem var. SQL Server 2014'te, kontrol ettiğim uzunluktan bağımsız olarak 1 satırlık bir tahmin görüyorum. Bu çok kötü planlar veriyor çünkü aslında binlerce hatta milyonlarca satır var ve SQL Server bu tabloyu iç içe bir döngünün dış tarafına koymayı seçiyor.

SQL Server 2012 31.622 satır tahmin ederken SQL Server 2014 için 1.0003 kardinalite tahmini için bir açıklama var mı? İyi bir geçici çözüm var mı?

İşte konunun kısa bir tekrarı:

-- Create a table with 1MM rows of dummy data
CREATE TABLE #customers (cust_nbr VARCHAR(10) NOT NULL)
GO

INSERT INTO #customers WITH (TABLOCK) (cust_nbr)
    SELECT TOP 1000000 
        CONVERT(VARCHAR(10),
        ROW_NUMBER() OVER (ORDER BY (SELECT NULL))) AS cust_nbr
    FROM master..spt_values v1
    CROSS JOIN master..spt_values v2
GO

-- Looking for string of a certain length.
-- While both CEs yield fairly poor estimates, the 2012 CE is much
-- more conservative (higher estimate) and therefore much more likely
-- to yield an okay plan rather than a drastically understimated loop join.
-- 2012: 31,622 rows estimated, 900K rows actual
-- 2014: 1 row estimated, 900K rows actual
SELECT COUNT(*)
FROM #customers
WHERE LEN(cust_nbr) = 6
OPTION (QUERYTRACEON 9481) -- Optionally, use 2012 CE
GO

İşte ek testler gösteren daha eksiksiz bir script

Ayrıca , SQL Server 2014 Kardinalite Tahmincisi'nin tanıtım belgesini de okudum , ancak durumu netleştiren bir şey bulamadım.

Yanıtlar:


20

Eski CE için, tahmin satır 3,16228% için bkz - ve bu sütun için kullanılan bir "sihirli numarası" sezgisel = değişmez temel fonksiyonlar (yüklem yapılarını temel alan diğer sezgisel vardır - ama LENiçin kolon etrafında sarılı eski CE sonuçları bu tahmin çerçevesiyle eşleşir). Bunun örneklerini Joe Sack İstatistikleri yokluğunda Seçicilik Guesses ve Ian Jose'nin Sabit-Sabit Karşılaştırma Tahmini üzerine bir yazı üzerinde görebilirsiniz .

-- Legacy CE: 31622.8 rows
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  ( QUERYTRACEON 9481); -- Legacy CE
GO

Şimdi yeni CE davranışına gelince, bu durum şu anda optimizer tarafından görülebiliyor gibi görünüyor (bu, istatistikleri kullanabileceğimiz anlamına gelir). Aşağıdaki hesap makinesinin çıktısına bakma alıştırmasını yaptım ve ilişkili otomatik üretim istatistiklerine bir işaretçi olarak bakabilirsiniz:

-- New CE: 1.00007 rows
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  ( QUERYTRACEON 2312 ); -- New CE
GO

-- View New CE behavior with 2363 (for supported option use XEvents)
SELECT  COUNT(*)
FROM    #customers
WHERE   LEN(cust_nbr) = 6
OPTION  (QUERYTRACEON 2312, QUERYTRACEON 2363, QUERYTRACEON 3604, RECOMPILE); -- New CE
GO

/*
Loaded histogram for column QCOL:
[tempdb].[dbo].[#customers].cust_nbr from stats with id 2
Using ambient cardinality 1e+006 to combine distinct counts:
  999927

Combined distinct count: 999927
Selectivity: 1.00007e-006
Stats collection generated:
  CStCollFilter(ID=2, CARD=1.00007)
      CStCollBaseTable(ID=1, CARD=1e+006 TBL: #customers)

End selectivity computation
*/

EXEC tempdb..sp_helpstats '#customers';


--Check out AVG_RANGE_ROWS values (for example - plenty of ~ 1)
DBCC SHOW_STATISTICS('tempdb..#customers', '_WA_Sys_00000001_B0368087');
--That's my Stats name yours is subject to change

Maalesef, mantık, LENfonksiyonun etkisi için ayarlanmayan farklı değerlerin bir tahminine dayanmaktadır .

Olası geçici çözüm

Her iki CE modelinde de aşağıdaki LENgibi yeniden yazarak trie tabanlı bir tahmin alabilirsiniz LIKE:

SELECT COUNT_BIG(*)
FROM #customers AS C
WHERE C.cust_nbr LIKE REPLICATE('_', 6);

GİBİ planı


Kullanılan İzleme Bayraklarına İlişkin Bilgiler:

  • 2363: yüklenmekte olan istatistikler dahil olmak üzere birçok bilgi gösterir.
  • 3604: DBCC komutlarının çıktısını mesajlar sekmesine yazdırır.

13

SQL 2012 için 1.0003 değerinde kardinalite tahmini ile ilgili bir açıklama var mı?

Sanırım @ Zane'nin cevabı bu bölümü oldukça iyi kapsar.

İyi bir geçici çözüm var mı?

Bu Hesaplanan Sütun için LEN(cust_nbr)Kümelenmemiş Bir Dizin için Persistanlı Olmayan bir Hesaplanmış Sütun oluşturmayı deneyebilirsiniz . Bu size doğru istatistikleri sağlamalıdır.

Bazı testler yaptım ve işte bulduklarım:

  • Istatist Olmayan Hesaplanan Sütununda, üzerinde hiçbir dizin tanımlanmadığında istatistikler otomatik olarak oluşturulur.
  • Kümelenmemiş Dizini Hesaplanan Sütun'a eklemek yalnızca yardımcı olmakla kalmadı, performansa da biraz zarar verdi. Biraz daha yüksek CPU ve geçen süreler. Biraz daha yüksek tahmini maliyet (buna değer).
  • Hesaplanan Sütunu PERSISTED(Dizinsiz) yapmak, diğer iki varyasyondan daha iyiydi. Tahmini Satırlar daha doğruydu. İşlemci ve geçen süre daha iyiydi (sıra başına bir şey hesaplamak zorunda olmadığından beklendiği gibi).
  • Hesaplanan Sütunda (Hesaplanan nedeniyle) Filtrelenmiş Dizin veya Filtrelenmiş İstatistikler oluşturamadım. Olsa bile PERSISTED:-(

1
Israrlı ve değil arasındaki derin karşılaştırma için teşekkürler Kalıcı hesaplanmış sütunun avantajları olsa bile, sürekli olmayan bir ifadenin istatistiklerinin yararlı olduğu bazı durumlarda çok az ek yük ile çok hızlı bir kazanç olabileceğini bilmek güzel.
Geoff Patterson
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.