क्या आप इस निष्पादन योजना की व्याख्या कर सकते हैं?


20

जब मैं इस चीज के बारे में आया तो मैं कुछ और शोध कर रहा था। मैं इसमें कुछ डेटा के साथ टेस्ट टेबल उत्पन्न कर रहा था और यह पता लगाने के लिए विभिन्न प्रश्नों को चला रहा था कि प्रश्नों को लिखने के विभिन्न तरीके निष्पादन योजना को कैसे प्रभावित करते हैं। यहां वह स्क्रिप्ट है जिसका उपयोग मैंने यादृच्छिक परीक्षण डेटा उत्पन्न करने के लिए किया था:

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('t') AND type in (N'U'))
DROP TABLE t
GO

CREATE TABLE t 
(
 c1 int IDENTITY(1,1) NOT NULL 
,c2 int NULL
) 
GO

insert into t
select top 1000000 a from
(select t1.number*2048 + t2.number a, newid() b
from [master]..spt_values t1 
cross join  [master]..spt_values t2
where t1.[type] = 'P' and t2.[type] = 'P') a
order by b
GO

update t set c2 = null
where c2 < 2048 * 2048 / 10
GO


CREATE CLUSTERED INDEX pk ON [t] (c1)
GO

CREATE NONCLUSTERED INDEX i ON t (c2)
GO

अब, इस डेटा को देखते हुए, मैंने निम्नलिखित प्रश्न को आमंत्रित किया:

select * 
from t 
where 
      c2 < 1048576 
   or c2 is null
;

मेरे महान आश्चर्य के लिए, इस क्वेरी के लिए जो निष्पादन योजना बनाई गई थी, वह यही थी । (बाहरी लिंक के लिए क्षमा करें, यह यहाँ फिट करने के लिए बहुत बड़ा है)।

क्या कोई मुझे समझा सकता है कि इन सभी " लगातार स्कैन " और " गणना स्केल " के साथ क्या हो रहा है? क्या हो रहा है?

योजना

  |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1010], [Expr1011], [Expr1012]))
       |--Merge Interval
       |    |--Sort(TOP 2, ORDER BY:([Expr1013] DESC, [Expr1014] ASC, [Expr1010] ASC, [Expr1015] DESC))
       |         |--Compute Scalar(DEFINE:([Expr1013]=((4)&[Expr1012]) = (4) AND NULL = [Expr1010], [Expr1014]=(4)&[Expr1012], [Expr1015]=(16)&[Expr1012]))
       |              |--Concatenation
       |                   |--Compute Scalar(DEFINE:([Expr1005]=NULL, [Expr1006]=NULL, [Expr1004]=(60)))
       |                   |    |--Constant Scan
       |                   |--Compute Scalar(DEFINE:([Expr1008]=NULL, [Expr1009]=(1048576), [Expr1007]=(10)))
       |                        |--Constant Scan
       |--Index Seek(OBJECT:([t].[i]), SEEK:([t].[c2] > [Expr1010] AND [t].[c2] < [Expr1011]) ORDERED FORWARD)

जवाबों:


29

निरंतर स्कैन प्रत्येक में एक कॉलम के साथ एकल-इन-मेमोरी पंक्ति का उत्पादन करता है। शीर्ष गणना स्केलर 3 कॉलम के साथ एक पंक्ति को आउटपुट करता है

Expr1005    Expr1006    Expr1004
----------- ----------- -----------
NULL        NULL        60

नीचे की गणना स्केलर 3 कॉलम के साथ एक पंक्ति को आउटपुट करता है

Expr1008    Expr1009    Expr1007
----------- ----------- -----------
NULL        1048576        10

संघचालक ऑपरेटर इन 2 पंक्तियों को एक साथ जोड़ता है और 3 कॉलमों को आउटपुट करता है लेकिन अब उनका नाम बदल दिया गया है

Expr1010    Expr1011    Expr1012
----------- ----------- -----------
NULL        NULL        60
NULL        1048576     10

Expr1012स्तंभ झंडे का एक सेट है आंतरिक रूप से उपयोग निश्चित परिभाषित करने के लिए स्टोरेज इंजन के लिए गुण की तलाश

2 पंक्तियों के साथ अगली गणना स्केलर

Expr1010    Expr1011    Expr1012    Expr1013    Expr1014    Expr1015
----------- ----------- ----------- ----------- ----------- -----------
NULL        NULL        60          True        4           16            
NULL        1048576     10          False       0           0      

अंतिम तीन कॉलम निम्नानुसार परिभाषित किए गए हैं और केवल मर्ज अंतराल ऑपरेटर को प्रस्तुत करने से पहले छँटाई प्रयोजनों के लिए उपयोग किए जाते हैं

[Expr1013] = Scalar Operator(((4)&[Expr1012]) = (4) AND NULL = [Expr1010]), 
[Expr1014] = Scalar Operator((4)&[Expr1012]), 
[Expr1015] = Scalar Operator((16)&[Expr1012])

Expr1014और Expr1015बस परीक्षण करें कि क्या कुछ बिट्स ध्वज में हैं। Expr1013एक बूलियन कॉलम को वापस लौटाता है, अगर दोनों के लिए बिट 4चालू है और Expr1010है NULL

क्वेरी में अन्य तुलना ऑपरेटरों की कोशिश करने से मुझे ये परिणाम मिलते हैं

+----------+----------+----------+-------------+----+----+---+---+---+---+
| Operator | Expr1010 | Expr1011 | Flags (Dec) |       Flags (Bin)       |
|          |          |          |             | 32 | 16 | 8 | 4 | 2 | 1 |
+----------+----------+----------+-------------+----+----+---+---+---+---+
| >        | 1048576  | NULL     |           6 |  0 |  0 | 0 | 1 | 1 | 0 |
| >=       | 1048576  | NULL     |          22 |  0 |  1 | 0 | 1 | 1 | 0 |
| <=       | NULL     | 1048576  |          42 |  1 |  0 | 1 | 0 | 1 | 0 |
| <        | NULL     | 1048576  |          10 |  0 |  0 | 1 | 0 | 1 | 0 |
| =        | 1048576  | 1048576  |          62 |  1 |  1 | 1 | 1 | 1 | 0 |
| IS NULL  | NULL     | NULL     |          60 |  1 |  1 | 1 | 1 | 0 | 0 |
+----------+----------+----------+-------------+----+----+---+---+---+---+

जिससे मैं अनुमान लगाता हूं कि बिट 4 का अर्थ है "रेंज की शुरुआत" (जैसा कि अप्रभावित होने का विरोध किया गया है) और बिट 16 का अर्थ है रेंज की शुरुआत समावेशी है।

यह 6 कॉलम परिणाम सेट SORTऑपरेटर द्वारा छांटे गए से उत्सर्जित होता है Expr1013 DESC, Expr1014 ASC, Expr1010 ASC, Expr1015 DESC। मान लिया जाये कि Trueका प्रतिनिधित्व करती है 1और Falseद्वारा 0पहले से प्रतिनिधित्व किया resultset इसी क्रम में पहले से ही है।

मेरी पिछली मान्यताओं के आधार पर इस तरह का शुद्ध प्रभाव निम्न क्रम में मर्ज अंतराल को प्रस्तुत करना है

 ORDER BY 
          HasStartOfRangeAndItIsNullFirst,
          HasUnboundedStartOfRangeFirst,
          StartOfRange,
          StartOfRangeIsInclusiveFirst

मर्ज अंतराल ऑपरेटर 2 पंक्तियों को आउटपुट करता है

Expr1010    Expr1011    Expr1012
----------- ----------- -----------
NULL        NULL        60
NULL        1048576     10

उत्सर्जित प्रत्येक पंक्ति के लिए एक सीमा की तलाश की जाती है

Seek Keys[1]: Start:[dbo].[t].c2 > Scalar Operator([Expr1010]), 
               End: [dbo].[t].c2 < Scalar Operator([Expr1011])

तो यह ऐसा प्रतीत होता है जैसे दो शुक्राणुओं का प्रदर्शन किया जाता है। एक जाहिरा तौर पर > NULL AND < NULLऔर एक > NULL AND < 1048576। हालाँकि, जो झंडे पास हो जाते हैं वे इसे IS NULLऔर < 1048576क्रमशः संशोधित करने के लिए दिखाई देते हैं। उम्मीद है @sqlkiwi इसे स्पष्ट कर सकती है और किसी भी अशुद्धि को सही कर सकती है!

यदि आप क्वेरी को थोड़ा बदल देते हैं

select *
from t 
where 
      c2 > 1048576 
   or c2 = 0
;

फिर योजना कई चाहने वाले सूचकांक के साथ बहुत सरल दिखती है।

योजना दिखाती है Seek Keys

Start: c2 >= 0, End: c2 <= 0, 
Start: c2 > 1048576

ओपी में मामले के लिए इस सरल योजना का उपयोग क्यों नहीं किया जा सकता है, इसके बारे में स्पष्टीकरण SQLKiwi द्वारा पहले के ब्लॉग पोस्ट पर टिप्पणियों में दिया गया है ।

कई विधेयकों के साथ एक अनुक्रमणिका की तलाश विभिन्न प्रकार की तुलना विधेय (यानी Isऔर Eqओपी में मामले में) को नहीं मिला सकती है । यह उत्पाद की सिर्फ एक वर्तमान सीमा है (और शायद यही कारण है कि अंतिम क्वेरी में समानता परीक्षण c2 = 0का उपयोग करके लागू किया गया है >=और <=इसके बजाय केवल क्वेरी के लिए आपको मिलने वाली सीधी समानता की तलाश है c2 = 0 OR c2 = 1048576


मैं पॉल के लेख में कुछ भी हाजिर नहीं कर सकता, जो [Expr1012] के झंडे में अंतर बताता है। क्या आप यह घटा सकते हैं कि 60/10 यहाँ क्या दर्शाता है?
मार्क स्टोरी-स्मिथ

@ MarkStorey-Smith - वे कहते हैं 62कि एक समानता तुलना के लिए है। मुझे लगता है कि 60है कि बजाय मतलब चाहिए > AND < के रूप में आप इस तथ्य प्राप्त में योजना में दिखाया गया है >= AND <=जब तक कि यह एक स्पष्ट है IS NULL(?) झंडा शायद या शायद थोड़ा 2कुछ और असंबंधित इंगित करता है और 60अभी भी जब मैं कर के रूप में समानता है set ansi_nulls offऔर के लिए इसे बदल c2 = nullपर अभी भी यह रहता है60
मार्टिन स्मिथ

2
@MartinSmith 60 वास्तव में NULL के साथ तुलना के लिए है। सीमा सीमा अभिव्यक्तियाँ NULL का उपयोग या तो अंत में 'अनबाउंड' का प्रतिनिधित्व करने के लिए करती हैं। तलाश हमेशा अनन्य होती है अर्थात प्रारंभ करें:> एक्सप्र एंड एंड: <एक्सक्लूसिव यूज>> = और <= का उपयोग करके। ब्लॉग टिप्पणी के लिए धन्यवाद, मैं उत्तर में एक उत्तर या एक लंबी टिप्पणी पोस्ट करूंगा (अभी यह न्याय करने के लिए बहुत देर हो चुकी है)।
पॉल व्हाइट GoFundMonica कहते

@ SQLKiwi - धन्यवाद। यह समझ आता है। उम्मीद है कि मैं पहले लापता कुछ बिट्स का पता लगा लिया होगा।
मार्टिन स्मिथ

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

13

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

गणना स्केलर ऑपरेटरों को NULL और मान 1045876 के साथ लोड किया जा रहा है, इसलिए वे डेटा को फ़िल्टर करने के प्रयास में स्पष्ट रूप से लूप जॉइन के साथ उपयोग करने जा रहे हैं।

वास्तव में अच्छा हिस्सा यह है कि यह योजना तुच्छ है। इसका मतलब है कि यह न्यूनतम अनुकूलन प्रक्रिया से गुजरा। सभी ऑपरेशन मर्ज अंतराल के लिए अग्रणी हैं। इसका उपयोग इंडेक्स सीक ( उस पर विवरण ) के लिए तुलना ऑपरेटरों का एक न्यूनतम सेट बनाने के लिए किया जाता है ।

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

ADDENDUM: वह आखिरी वाक्य बंद है। दो तलाश थे। मैंने योजना को गलत बताया। बाकी अवधारणाएं समान हैं और लक्ष्य, न्यूनतम पास, समान है।

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