फ़िल्टर किए गए इंडेक्स का उपयोग केवल तब किया जाता है जब फ़िल्टर किया गया भाग JOIN में न हो, जहां नहीं हो


10

हालाँकि मैंने नीचे फ़िल्टर किए गए इंडेक्स को बनाया है, जब मैं 2 प्रश्नों को आगे चलाता हूं, तो इस इंडेक्स को केवल पहले उदाहरण में तलाश के लिए उपयोग किया जाता है, जिसमें जोइन में END_DTTM होता है, जहां क्लॉज के बजाय यह होता है (यह प्रश्नों में एकमात्र अंतर है) । क्या कोई समझा सकता है कि ऐसा क्यों होता है?

सूचकांक निर्माण

CREATE NONCLUSTERED INDEX [ix_PATIENT_LIST_BESPOKE_LIST_ID_includes] ON [dbo].[PATIENT_LIST_BESPOKE] 
(
    [LIST_ID] ASC,
    [END_DTTM] ASC
)
WHERE ([END_DTTM] IS NULL)

प्रश्नों

DECLARE @LIST_ID INT = 3655

--This one seeks on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
                                      AND PATIENT_LIST_BESPOKE.END_DTTM IS NULL
WHERE
    PATIENT_LISTS.LIST_ID = @LIST_ID

--This one scans on the index

SELECT  
    PATIENT_LISTS.LIST_ID
FROM    
    DBO.PATIENT_LISTS
    LEFT JOIN DBO.PATIENT_LIST_BESPOKE ON PATIENT_LISTS.LIST_ID = PATIENT_LIST_BESPOKE.LIST_ID  
WHERE   
    PATIENT_LISTS.LIST_ID = @LIST_ID AND
    PATIENT_LIST_BESPOKE.END_DTTM IS NULL   

जवाबों:


12

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

बहुत सरल करने के लिए, भौतिक सूचकांक रणनीति कार्यान्वयन यह करता है:

Predicate + Logical Get -> Physical Get (using Index)

जिस क्वेरी में आप रुचि रखते हैं वह बाहरी जुड़ाव के ऊपर विधेय से शुरू होती है:

Predicate on T2 --+-- LOJ -- Get (T1)
                       |
                       +---- Get (T2)

यह आकार इंडेक्स स्ट्रेटेजी नियम से मेल नहीं खाता है क्योंकि विधेय गेट के समीप नहीं है। तो, जवाब का पहला हिस्सा यह है कि फ़िल्टर किए गए इंडेक्स मिलान तब तक विफल हो जाएंगे जब तक कि विधेय को बाहरी जुड़ाव से आगे नहीं बढ़ाया जा सकता है।

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

परिणामस्वरूप, फ़िल्टर किए गए अनुक्रमणिका का मिलान इस मामले में विफल हो जाता है। स्पष्ट होने के लिए, फिर से लिखना बहुत ही विशिष्ट मामले में मान्य होगा जो आप उद्धृत करते हैं (दूसरी क्वेरी)।

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

पृष्ठभूमि और आगे की जानकारी:


9

ये हैं नहीं के बाद से एक से पहले के बाद अन्य कर सकते हैं फिल्टर में शामिल होने फ़िल्टर कर सकते हैं शब्दार्थ ही प्रश्नों,। मुझे एक सरल उदाहरण के साथ स्पष्ट करें:

CREATE TABLE dbo.Lefty(LeftyID INT PRIMARY KEY);

CREATE TABLE dbo.Righty(LeftyID INT, SomeList INT);

INSERT dbo.Lefty(LeftyID) VALUES(1),(2),(3);

INSERT dbo.Righty(LeftyID, SomeList) VALUES(1,1),(1,NULL),(2,2);

प्रश्न 1 सभी तीन पंक्तियों को लौटाता है:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
AND r.SomeList IS NULL;

क्वेरी 2, हालांकि, लेफ्टी 2 को छोड़ देता है:

SELECT l.LeftyID, r.SomeList
FROM dbo.Lefty AS l
LEFT OUTER JOIN dbo.Righty AS r
ON l.LeftyID = r.LeftyID
WHERE r.SomeList IS NULL;

SQLfiddle सबूत

यदि आप एंटी-सेमी जॉइन करने की कोशिश कर रहे हैं, तो परीक्षण किए गए कॉलम को अशक्त नहीं होना चाहिए । जब आप INNER के साथ काम कर रहे हैं तो ON और WHERE के बीच बढ़ते मापदंड कोई तार्किक अंतर नहीं बनाते हैं, लेकिन OUTER के साथ एक महत्वपूर्ण अंतर है। और आपको अधिक ध्यान रखना चाहिए कि आपके परिणाम सही हैं या नहीं एक फ़िल्टर किए गए सूचकांक का भी उपयोग किया जा सकता है।


उत्तर के लिए धन्यवाद, लेकिन मैं दावा नहीं कर रहा हूं कि प्रश्न समान हैं, मैं पूछ रहा हूं कि एक क्वेरी फ़िल्टर किए गए सूचकांक का उपयोग क्यों करता है और दूसरा नहीं करता है।
क्रिस

@chris क्या आपने एक इंडेक्स इंडेक्स के साथ उस इंडेक्स को मजबूर करने की कोशिश की? मैं उस संकेत के साथ और उसके बिना वास्तविक, निष्पादन के बाद की योजनाओं की तुलना करने के लिए उत्सुक हूं। मेरे लिए यह स्पष्ट है कि ऑप्टिमाइज़र उस इंडेक्स को अनदेखा कर रहा है जब यह मानता है कि यह एक एंटी-सेमी जॉइन कर रहा है (क्योंकि यह उस मामले में एक अशक्त स्तंभ का उपयोग करने की उम्मीद नहीं करेगा), लेकिन मुझे यकीन नहीं है कि अगर यह सुनिश्चित हो संचालन की लागत या आदेश या कुछ अंतर्निहित ज्ञान के साथ करते हैं कि संभवतः बाईं ओर से आने वाली पंक्तियों की तुलना में बहुत अधिक पंक्तियां हैं जो फ़िल्टर्ड इंडेक्स में हैं। योजनाओं को देखने से मदद मिल सकती है।
हारून बर्ट्रेंड

3

दो प्रश्न अलग-अलग हैं - अर्थ और परिणाम में। यहाँ एक पुनर्लेखन है, इसलिए यह अधिक स्पष्ट है कि दोनों प्रश्न क्या कर रहे हैं:

-- 1st query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    LEFT JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID ;           -- and the join

और 2:

-- 2nd query
SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
    JOIN  
      ( SELECT LIST_ID                    -- the filtered index
        FROM   DBO.PATIENT_LIST_BESPOKE   -- can be used
        WHERE  END_DTTM IS NULL           -- for the subquery
      ) AS b
    ON  a.LIST_ID = b.LIST_ID             -- and the join

UNION ALL

SELECT  
    a.LIST_ID
FROM    
      ( SELECT LIST_ID 
        FROM   DBO.PATIENT_LISTS
        WHERE  LIST_ID = @LIST_ID
      ) AS a
WHERE NOT EXISTS  
      ( SELECT *
        FROM   DBO.PATIENT_LIST_BESPOKE AS b
        WHERE  a.LIST_ID = b.LIST_ID         -- but not for this
      ) ;

मुझे लगता है कि अब यह बहुत स्पष्ट है कि 2nq क्वेरी के दूसरे भाग के लिए, फ़िल्टर किए गए इंडेक्स का उपयोग नहीं किया जा सकता है।


इन प्रश्नों के बारे में विस्तार से, LIST_IDपहली तालिका में 4 प्रकार के मूल्य हैं:

  • (ए) मान जिसमें दूसरी तालिका में सभी के साथ मिलान पंक्तियाँ हैं END_DTTM IS NULL

  • (बी) मान जिसमें दूसरी तालिका में END_DTTM IS NULLऔर साथ , दोनों के साथ मिलान पंक्तियाँ हैं END_DTTM IS NOT NULL

  • (c) मान जिसमें दूसरी तालिका में सभी के साथ मिलान पंक्तियाँ हों END_DTTM IS NOT NULL

  • (d) दूसरी तालिका में कोई मिलान पंक्तियाँ नहीं है।

अब, पहली क्वेरी प्रकार (ए) और (बी) के सभी मानों को संभवतः कई बार लौटा देगी (जैसे कि उनके पास दूसरी तालिका में एक मिलान पंक्ति है END_DTTM IS NULL) और सभी प्रकार की पंक्तियाँ (सी) और (डी) बिल्कुल एक बार ( यह बाहरी जुड़ाव का गैर-मिलान हिस्सा है)।

दूसरी क्वेरी संभवतया कई बार (ए) और (बी) के सभी मान लौटाएगी (जैसे कि उनके साथ दूसरी तालिका में एक मिलान पंक्ति होती है END_DTTM IS NULL) और प्रकार की सभी पंक्तियाँ (डी) बिल्कुल एक बार।
यह टाइप (c) के किसी भी मान को वापस नहीं करेगा क्योंकि सम्मिलित होने पर दूसरी तालिका में मिलान पंक्तियाँ मिलेंगी (लेकिन ये होंगी END_DTTM IS NOT NULL) और उन्हें बाद के WHEREखंड द्वारा हटा दिया जाएगा ।

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