SQL सर्वर 2016 में SUBSTRING () युक्त विधेय पर अनुमानों में परिवर्तन


13

SQL सर्वर 2016 में परिवर्तन के बारे में कोई दस्तावेज या शोध है कि SUBSTRING () या अन्य स्ट्रिंग फ़ंक्शंस वाले विधेय के लिए कार्डिनैलिटी का अनुमान कैसे लगाया जाता है?

मेरे द्वारा पूछे जाने का कारण यह है कि मैं एक ऐसी क्वेरी को देख रहा था, जिसका प्रदर्शन संगतता मोड 130 में कम हो गया था और इसका कारण उन पंक्तियों की संख्या के अनुमान में बदलाव से संबंधित था जो एक WHERE क्लॉज से मेल खाती हैं जिसमें SUBSTRING () के लिए कॉल था। मैंने प्रश्न को फिर से लिखने के साथ समस्या को ठीक किया, लेकिन आश्चर्य है कि क्या किसी को SQL Server 2016 में इस क्षेत्र में परिवर्तन के बारे में किसी भी दस्तावेज के बारे में पता है।

डेमो कोड नीचे है। इस परीक्षण मामले में अनुमान बहुत करीब हैं, लेकिन डेटा के आधार पर सटीकता भिन्न होती है।

परीक्षण के मामले में, हमवतन स्तर 120 में, SQL सर्वर अनुमान के लिए हिस्टोग्राम का उपयोग करता हुआ दिखाई देता है, जबकि हमवतन स्तर में 130 SQL सर्वर तालिका मैचों का एक निश्चित 10% मान लेता है।

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
विशेष प्रश्न के बारे में निश्चित नहीं है, लेकिन अगर Y5_EG3तार सिर्फ कोड हैं और हमेशा ऊपरी-मामले हैं, तो आप हमेशा एक द्विआधारी टकराव को निर्दिष्ट करने की कोशिश कर सकते हैं - Latin1_General_100_BIN2- जो फ़िल्टर संचालन पर गति में सुधार करना चाहिए। बस जोड़ने COLLATE Latin1_General_100_BIN2के लिए CREATE TABLEबस के बाद, बयान varchar(15)। मुझे यह देखने की उत्सुकता होगी कि क्या इसने योजना निर्माण / अनुमान को भी प्रभावित किया है।
सोलोमन रटज़की

जवाबों:


8

मुझे किसी भी दस्तावेज के बारे में पता नहीं है। मैंने इस पर ध्यान दिया और कुछ टिप्पणियां कीं जो कि एक टिप्पणी के लिए बहुत लंबी हैं।

10% अनुमान हमेशा गिरावट नहीं है। निम्नलिखित उदाहरण लें।

TRUNCATE TABLE dbo.StringTest

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

और WHEREआपके प्रश्न में खंड।

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

तालिका में एक लाख पंक्तियाँ हैं। ये सभी विधेय से मेल खाते हैं। हमवतन स्तर 130 के तहत 10% अनुमान में 100,000 का अनुमान लगाया जाता है। 120 के तहत अनुमानित पंक्तियों 1.03913 है।

120 व्यवहार हिस्टोग्राम का उपयोग करता है लेकिन केवल अलग-अलग पंक्तियों की संख्या प्राप्त करने के लिए। मेरे मामले में घनत्व वेक्टर 1.039131E-06 से पता चलता है और यह अनुमानित पंक्ति गणना प्राप्त करने के लिए तालिका कार्डिनैलिटी से गुणा किया जाता है। सभी मूल्य वास्तव में भिन्न हैं लेकिन सभी विधेय से मेल खाते हैं।

query_optimizer_estimate_cardinalityविस्तारित ईवेंट को ट्रेस करने से पता चलता है कि 130 के तहत दो अलग-अलग <StatsCollection Name="CStCollFilter"ईवेंट हैं। पहले एक अनुमान में 100,000। दूसरा हिस्टोग्राम लोड करता है और 1.04 अनुमान प्राप्त करने के लिए CSelCalcPointPredsFreqBased / DistinctCountCalculator का उपयोग करता है। यह दूसरा परिणाम अप्रयुक्त प्रतीत होता है।

आपके द्वारा देखा गया व्यवहार 130 में लगातार लागू नहीं होता है। मैंने ORDER BY TheString130 अनुमानक के लिए एक स्पष्ट जीत की उम्मीद करते हुए जोड़ा क्योंकि 120 एक पंक्ति के लिए मेमोरी अनुदान के साथ संघर्ष करता है लेकिन यह मामूली बदलाव अनुमानित पंक्तियों को नीचे लाने के लिए पर्याप्त था। 130 मामले में भी 1.03913।

जोड़ा जा रहा है OPTION (QUERYRULEOFF SelectToFilter)वापस आ जाता अनुमान 1,00,000 प्रकार में जा रहा है, लेकिन स्मृति अनुदान में वृद्धि नहीं करता और तरह बाहर आ अनुमान अभी भी तालिका अलग मूल्यों पर आधारित हैं।

यहाँ छवि विवरण दर्ज करें

इसी तरह समानता के लिए लागत सीमा को कम करना ताकि क्वेरी को एक समानांतर योजना मिल जाए, 130 मामलों में कम अनुमान पर वापस लौटने के लिए पर्याप्त थी। जोड़ना QUERYTRACEON 8757भी कम अनुमान का कारण बनता है। ऐसा लगता है कि 10% का अनुमान केवल तुच्छ योजनाओं के लिए रखा गया है।

आपका प्रस्तावित पुनर्लेखन

WHERE TheString LIKE 'ZZ[_]%'

दोनों को बहुत बेहतर अनुमान दिखाता है। इसके लिए आउटपुट है

  CSelCalcTrieBased

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

दिखा रहा है कि यह कोशिश करता था । इसके बारे में अधिक जानकारी स्ट्रिंग सारांश आँकड़े अनुभाग में यहाँ ऊपर है

हालाँकि यह आपकी मूल क्वेरी के समान नहीं है। जैसा कि पहले उदाहरण है _कि अब गतिशील रूप से पाए जाने के बजाय हमेशा तीसरे वर्ण का होना चाहिए।

यदि यह धारणा आपके मूल प्रश्न में हार्डकोड है

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

अनुमान विधि बदल जाती है CSelCalcHistogramComparison(INTERVAL)और अनुमानित पंक्तियाँ सटीक हो जाती हैं।

यह एक सीमा में परिवर्तित करने में सक्षम है

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

और उस श्रेणी के मानों के साथ पंक्तियों की संख्या का अनुमान लगाने के लिए हिस्टोग्राम का उपयोग करें।

हालांकि यह केवल कार्डिनैलिटी अनुमान पर लागू होता है। LIKEयह बेहतर है क्योंकि यह रनटाइम के दौरान रेंज की तलाश कर सकता है। SUBSTRING(TheString, 1, 3)या LEFT(TheString, 3)नहीं कर सकते।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.