SQL सर्वर कार्डिनैलिटी संकेत


14

क्या SQL सर्वर अनुकूलक (किसी भी संस्करण) के लिए एक कार्डिनैलिटी अनुमान को 'इंजेक्ट' करने का एक तरीका है?

यानी ओरेकल के कार्डिनैलिटी संकेत के समान कुछ।

मेरी प्रेरणा लेख द्वारा संचालित है, कितने अच्छे हैं क्वेरी ऑप्टिमाइज़र, वास्तव में? [१] , जहाँ वे खराब योजना के चयन पर कार्डिनलिटी एस्टीमेटर के प्रभाव का परीक्षण करते हैं। इसलिए, यह पर्याप्त होगा यदि मैं SQL सर्वर को जटिल प्रश्नों के लिए कार्डिनैलिटी को सटीक रूप से 'अनुमान' करने के लिए मजबूर कर सकता हूं।


[१] लेईस, विक्टर, एट अल। "क्वेरी ऑप्टिमाइज़र वास्तव में कितने अच्छे हैं?"
VLDB बंदोबस्ती 9.3 (2015) की कार्यवाही: 204-215।

जवाबों:


10

आप CARDINALITYरणनीतिक रूप से उपयोग करके TOPऔर MANY() एडम मैकैनिक द्वारा विकसित उपयोगकर्ता परिभाषित फ़ंक्शन द्वारा ओरेकल के संकेत के समान कुछ प्राप्त कर सकते हैं । आइए कुछ उदाहरणों के माध्यम से काम करते हैं। मैं आज़ादी से उपलब्ध एडवेंचरवर्क्स डेटाबेस का उपयोग कर रहा हूँ। मान लें कि मुझे वास्तव thमें निम्नलिखित क्वेरी में व्युत्पन्न तालिका द्वारा लौटी पंक्तियों की संख्या को नियंत्रित करने की आवश्यकता है :

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID;

जैसा कि, मुझे 113443 पंक्तियों का अनुमान है:

क्वेरी शुरू करना

यदि मुझे एक पंक्ति लक्ष्य निर्धारित करने के लिए क्वेरी संकेत के साथ thउपयोग कर सकने वाले अनुमान से कम करने की आवश्यकता है । यहाँ यह करने का एक तरीका है:TOPOPTIMIZE FOR

DECLARE @row_goal BIGINT = 9223372036854775807;
SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1));

हम देख सकते हैं कि अनुमान सिर्फ 1 पंक्ति है:

1 पंक्ति का अनुमान

परिणामों को बदलने से बचने के @row_goalलिए मैंने सबसे बड़ा संभव BIGINTमूल्य निर्धारित किया है । OPTIMIZE FORक्वेरी संकेत क्वेरी अनुकूलक करने के लिए अनुकूलक का निर्देश के रूप में अगर @row_goalयह है 1. के बराबर मैं एक ही परिणाम प्राप्त होगा, लेकिन क्वेरी अलग ढंग से अनुकूलित कर दिया जाएगा।

कार्डिनैलिटी का अनुमान बढ़ाना मुश्किल है। हम इसके लिए केवल मूल्य नहीं बढ़ा सकते TOPक्योंकि ऑप्टिमाइज़र को एहसास होगा कि यह पर्याप्त पंक्तियों को वापस नहीं करेगा। हालांकि, हम MANY()अनुमान में पंक्तियों को जोड़ने के लिए फ़ंक्शन का उपयोग कर सकते हैं । ध्यान दें कि MANY()फ़ंक्शन हमेशा 0 पंक्तियों को लौटाएगा लेकिन इसमें से पंक्ति अनुमान इनपुट पैरामीटर के साथ बदलता है। मान लीजिए कि आपको व्युत्पन्न तालिका से पंक्ति अनुमान को 10X तक बढ़ाने की आवश्यकता है। इसे पूरा करने का एक तरीका:

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (9223372036854775807) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON 1=1
) th ON p.ProductID = th.ProductID;

हम देख सकते हैं कि अनुमान 10X बेस टेबल है:

10X क्वेरी

अतृप्त TOP को को चारों ओर की तालिकाओं को हिलाने से रोकने के लिए जोड़ा गया था। इसके बिना MANY()फ़ंक्शन को योजना में गलत स्थान पर लागू किया जा सकता है।

यदि आप एक कारक द्वारा पंक्तियों की संख्या को गुणा करने के बजाय एक सटीक overestimate चाहते हैं, तो दो तकनीकों को संयोजित करना संभव है। उदाहरण के लिए, मान लें कि आपको वास्तव में व्युत्पन्न तालिका के अनुमान की आवश्यकता 1000000 पंक्तियों के बराबर है। इसे पूरा करने का एक तरीका:

DECLARE @row_goal BIGINT = 9223372036854775807;

SELECT 
    p.Name
    , th.ProductId
    , th.Quantity
    , th.ActualCost
FROM Production.Product p
INNER JOIN (
    SELECT TOP (@row_goal) ProductId, Quantity, ActualCost
    FROM Production.TransactionHistory 
    LEFT OUTER JOIN dbo.Many(10) AS m ON
        1=1
) th ON p.ProductID = th.ProductID
OPTION (OPTIMIZE FOR (@row_goal = 1000000));

हम देख सकते हैं कि अनुमान 1000000 पंक्तियाँ हैं:

1 एम पंक्तियों

मुझे आपको सावधान करने की आवश्यकता है कि ये उन्नत तकनीकें हैं जिन्हें अक्सर क्वेरी अनुकूलन के लिए आवश्यक नहीं है। यदि आप अधिक जानना चाहते हैं, तो मैं एडम मैकहानिक द्वारा प्रस्तुत रो गोल की क्लैश देखने की सलाह देता हूं ।


dbo.Many फ़ंक्शन

-- By Adam Machanic, reproduced with permission
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'Many' AND OBJECT_SCHEMA_NAME(object_id) = 'dbo')
    DROP FUNCTION dbo.Many
GO
CREATE FUNCTION dbo.Many(@n INT)
RETURNS TABLE AS
RETURN
(
    WITH
    a(x) AS
    (
        SELECT
            *
        FROM
        (
            VALUES
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1),
                (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)
        ) AS x0(x)
    )
    SELECT TOP(@n)
        1 AS x
    FROM
        a AS a1,
        a AS a2
    WHERE
        a1.x % 2 = 0
)
GO

9

ऑप्टिमाइज़र में सीधे तौर पर कार्डिनैलिटी के अनुमान को इंजेक्ट करने का कोई तरीका नहीं है लेकिन आप जो हासिल करना चाहते हैं उसके आधार पर कुछ विकल्प हैं।

आप OPTION (FAST N)पंक्ति लक्ष्यों को प्रस्तुत करने के लिए एक क्वेरी संकेत का उपयोग कर सकते हैं , और संभवतः इंजेक्शन के लिए सीटीई या उप-श्रेणियों का उपयोग करके अपनी क्वेरी को फिर से लिख सकते हैंTOP...ORDER BY अपने निष्पादन योजना के विभिन्न भागों में पंक्तिबद्ध लक्ष्यों , लेकिन मुझे यकीन नहीं है कि जब आप शुरू करेंगे तो आपकी परिणामी क्वेरी कितनी कुशल होगी अधिक जटिल निर्माणों के साथ खेलना।

ऑप्टिमाइज़र के अंदर देखें : गहराई में पंक्ति लक्ष्य अधिक गहन स्पष्टीकरण के लिए ।

अनुकूलक की पसंद आप की जरूरत नहीं है प्रमुखता अनुमानों इंजेक्षन करने की कोशिश करने के लिए है कि आप ऑपरेटरों को प्रभावित करना चाहते हैं, लेकिन आप जैसी चीजों के इस्तेमाल कर सकते हैं OPTION (MERGE JOIN)याOPTION (HASH JOIN) भौतिक संचालकों को बाध्य करने के लिए उदाहरण के लिए ।

यह लेख संकेत का उपयोग करके किसी योजना को प्रभावित करने के तरीके के बारे में अधिक विस्तार से बताता है: संकेत के साथ निष्पादन योजनाओं को नियंत्रित करना

यदि आप एक योजना को ठीक करना चाहते हैं तो आप एक योजना गाइड का उपयोग भी कर सकते हैं।

फिर से यह स्पष्ट नहीं है कि आपका वास्तविक उपयोग मामला क्या है, और आपका माइलेज इन तकनीकों का उपयोग करके भिन्न हो सकता है। कई मामलों में यह बेहतर है कि आप केवल आशावादी निर्णय लें, और सुनिश्चित करें कि आपके पास तारीख तक के आंकड़े हैं, इसलिए आशावादी एक सूचित निर्णय ले सकता है।


प्रासंगिक Microsoft कनेक्ट सुझाव: Xor88 द्वारा प्रश्नों में फ़िल्टर चयनात्मकता संकेत निर्दिष्ट करने की अनुमति दें । Microsoft ने जवाब दिया:

प्रतिक्रिया के लिए धन्यवाद। मैं इसका संभावित लाभ देख सकता हूं। सामान्य तौर पर हम अपने स्वचालित व्यवहार को यथासंभव बेहतर बनाने की कोशिश करते हैं और इस तरह के संकेत की आवश्यकता से बचते हैं, लेकिन निश्चित रूप से हमारे पास कई अन्य संकेत हैं। हम इसे भविष्य में रिलीज के लिए विचार करेंगे लेकिन यह डेनाली (11.0) संस्करण से परे होगा।

सादर,
एरिक हैनसन
प्रोग्राम मैनेजर
SQL सर्वर क्वेरी प्रोसेसिंग


3

आप OPTIMIZE FORसंकलन के दौरान वास्तविक मान (पैरामीटर) या अज्ञात मान (चर) का उपयोग करने के बजाय संकेतित मूल्यों के आधार पर कार्डिनैलिटी अनुमान का अनुमान लगाने के लिए SQL सर्वर क्वेरी संकेत का उपयोग कर सकते हैं । क्वेरी संकेत विषय देखें पूर्ण विवरण के लिए एसक्यूएल सर्वर दस्तावेज में।

उदाहरण के लिए, नीचे दी गई क्वेरी, कुल औसत कार्डिनलिटी के बजाय संकेत मानों से हिस्टोग्राम के आधार पर पंक्ति गणना का अनुमान लगाएगी क्योंकि यह अन्यथा स्थानीय चर के साथ होगा।

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
SELECT *
FROM dbo.Example
WHERE
    DateColumn BETWEEN  @StartDate AND @EndDate
OPTION(OPTIMIZE FOR(@StartDate = '20100101', @EndDate='20100101'));

इसी तरह, संकेत का उपयोग मापदंडों के लिए किया जा सकता है ताकि अनुमान संकलन के दौरान वास्तविक पैरामीटर मानों के बजाय संकेत मानों से हिस्टोग्राम पर आधारित हो।

DECLARE 
      @StartDate datetime = '20150101'
    , @EndDate datetime = '20150102';
EXECUTE sp_executesql N'SELECT *
        FROM dbo.Example
        WHERE
            DateColumn BETWEEN  @StartDate AND @EndDate
        OPTION(OPTIMIZE FOR(@StartDate = ''20100101'', @EndDate=''20100101''));'
    , N'@StartDate datetime, @EndDate datetime'
    , @StartDate = @StartDate
    , @EndDate = @EndDate;

UNKNOWNकीवर्ड के वास्तविक पैरामीटर मान और आँकड़े हिस्टोग्राम के आधार पर आकलन के समग्र औसत प्रमुखता उपयोग करने के लिए संकेत में एक शाब्दिक के बजाय निर्दिष्ट किया जा सकता।

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