SQL सर्वर 2014 में LEN () बुरी तरह से कार्डिनैलिटी को कम क्यों करता है?


26

मेरे पास एक स्ट्रिंग स्तंभ और एक विधेय के साथ एक तालिका है जो एक निश्चित लंबाई के साथ पंक्तियों की जांच करता है। SQL सर्वर 2014 में, मैं उस लंबाई की परवाह किए बिना 1 पंक्ति का अनुमान देख रहा हूं, जिसकी मैं जांच कर रहा हूं। यह बहुत खराब योजनाओं की उपज है क्योंकि वास्तव में हजारों या यहां तक ​​कि लाखों पंक्तियां हैं और SQL सर्वर इस तालिका को नेस्टेड लूप के बाहरी तरफ रखना चुन रहा है।

SQL Server 2014 के लिए 1.0003 के कार्डिनैलिटी अनुमान के लिए स्पष्टीकरण है, जबकि SQL Server 2012 में 31,622 पंक्तियों का अनुमान है? क्या कोई अच्छा समाधान है?

यहाँ इस मुद्दे का एक छोटा प्रजनन है:

-- 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

यहां अतिरिक्त परीक्षणों को दिखाने वाली एक पूरी स्क्रिप्ट है

मैंने SQL Server 2014 कार्डिनैलिटी एस्टीमेटर पर व्हाइटपेपर भी पढ़ा है , लेकिन वहां ऐसा कुछ नहीं मिला जिससे स्थिति स्पष्ट हो।

जवाबों:


20

विरासत सीई के लिए, मुझे लगता है कि अनुमान पंक्तियों के 3.16228% के लिए है - और यह एक "जादू की संख्या" है जिसका उपयोग स्तंभ के लिए उपयोग किया जाता है = शाब्दिक रूप से भविष्यवाणी की जाती है (विधेय निर्माण के आधार पर अन्य उत्तराधिकार हैं - लेकिन LENस्तंभ के लिए स्तंभ के चारों ओर लिपटा हुआ है) विरासत CE परिणाम इस अनुमान-रूपरेखा से मेल खाते हैं)। आप इसका उदाहरण उदहारण के आधार पर एक पोस्ट पर देख सकते हैं , जो सैक द्वारा सांख्यिकी की अनुपस्थिति में , और इयान जोस द्वारा निरंतर-निरंतर तुलना अनुमान

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

अब नए CE व्यवहार के लिए, ऐसा लग रहा है कि यह अब ऑप्टिमाइज़र (जिसका अर्थ है कि हम आँकड़ों का उपयोग कर सकते हैं) के लिए दिखाई दे रहे हैं। मैं नीचे दिए गए कैलकुलेटर आउटपुट को देखने के अभ्यास के माध्यम से गया, और आप एक संकेतक के रूप में संबंधित ऑटो-जेनरेशन के आंकड़ों को देख सकते हैं:

-- 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

दुर्भाग्य से तर्क अलग-अलग मूल्यों की संख्या के अनुमान पर निर्भर करता है, जो LENफ़ंक्शन के प्रभाव के लिए समायोजित नहीं होता है ।

संभव समाधान

आप एक के LENरूप में फिर से लिखना द्वारा दोनों सीई मॉडल के तहत एक त्रि-आधारित अनुमान प्राप्त कर सकते हैं LIKE:

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

योजना की तरह


ट्रेस फ्लैग्स पर प्रयुक्त जानकारी:

  • 2363: बहुत सी जानकारी दिखाता है, जिसमें आंकड़े लोड किए जा रहे हैं।
  • 3604: संदेश टैब पर DBCC कमांड के आउटपुट को प्रिंट करता है।

13

SQL 2014 के लिए 1.0003 के कार्डिनैलिटी अनुमान के लिए स्पष्टीकरण है जबकि SQL 2012 में 31,622 पंक्तियों का अनुमान है?

मुझे लगता है कि @ ज़ेन का जवाब इस हिस्से को बहुत अच्छी तरह से कवर करता है।

क्या कोई अच्छा समाधान है?

आप एक गैर-सुसंगत कम्प्यूटेड कॉलम बनाने की कोशिश कर सकते हैं LEN(cust_nbr)और (वैकल्पिक रूप से) उस कम्प्यूट कॉलम पर एक गैर-क्लस्टर सूचकांक बना सकते हैं। आपको सटीक आँकड़े प्राप्त करने चाहिए।

मैंने कुछ परीक्षण किया और यहाँ मैंने पाया है:

  • जब गैर-अनुक्रमित गणना कॉलम पर आंकड़े बनाए गए थे, तो उस पर कोई सूचकांक परिभाषित नहीं किया गया था।
  • कम्प्यूटेड कॉलम पर नॉन-क्लस्टर्ड इंडेक्स को जोड़ने से न केवल मदद मिली, इससे वास्तव में प्रदर्शन को थोड़ा नुकसान पहुंचा। थोड़ा अधिक सीपीयू और बीता हुआ समय। थोड़ा अधिक अनुमानित लागत (जो भी इसके लायक है)।
  • PERSISTED(कोई अनुक्रमणिका) के रूप में कम्प्यूटेड कॉलम बनाना अन्य दो विविधताओं से बेहतर था। अनुमानित पंक्तियाँ अधिक सटीक थीं। सीपीयू और बीता हुआ समय बेहतर था (जैसा कि अपेक्षित था क्योंकि इसे प्रति पंक्ति कुछ भी गणना करने की आवश्यकता नहीं थी)।
  • मैं गणना किए गए कॉलम पर फ़िल्टर किए गए इंडेक्स या फ़िल्टर किए गए आंकड़े बनाने में असमर्थ था (इसके कारण गणना की जा रही थी), भले ही वह था PERSISTED:-(

1
निरंतर और नहीं के बीच पूरी तरह से तुलना के लिए धन्यवाद। यह जानना अच्छा है कि भले ही लगातार गणना किए गए कॉलम के अपने फायदे हों, लेकिन गैर-निरंतर कुछ मामलों में बहुत कम ओवरहेड के साथ एक बहुत तेज जीत हो सकती है जहां एक अभिव्यक्ति पर आंकड़े फायदेमंद होते हैं।
ज्योफ पैटरसन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.