एसक्यूएल सर्वर ए <बी या ए <बी या ए> बी में विभाजित करता है, यदि बी गैर-नियतात्मक है तो अजीब परिणाम देता है


26

हमें SQL सर्वर के साथ एक दिलचस्प समस्या का सामना करना पड़ा है। निम्नलिखित रेप्रो उदाहरण पर विचार करें:

CREATE TABLE #test (s_guid uniqueidentifier PRIMARY KEY);
INSERT INTO #test (s_guid) VALUES ('7E28EFF8-A80A-45E4-BFE0-C13989D69618');

SELECT s_guid FROM #test
WHERE s_guid = '7E28EFF8-A80A-45E4-BFE0-C13989D69618'
  AND s_guid <> NEWID();

DROP TABLE #test;

बेला

कृपया एक क्षण के लिए भूल जाएं कि s_guid <> NEWID()स्थिति पूरी तरह से बेकार लगती है - यह सिर्फ एक न्यूनतम रिप्रोड उदाहरण है। चूंकि NEWID()कुछ दिए गए स्थिर मूल्य के मिलान की संभावना बेहद कम है, इसलिए इसे हर बार TRUE का मूल्यांकन करना चाहिए।

लेकिन यह नहीं है। इस क्वेरी को चलाना आमतौर पर 1 पंक्ति देता है, लेकिन कभी-कभी (10 से 1 बार से अधिक बार) 0 पंक्तियों को लौटाता है। मैंने अपने सिस्टम पर SQL Server 2008 के साथ इसे पुन: पेश किया है, और आप इसे ऊपर दिए गए लिंक (SQL Server 2014) के साथ ऑन-लाइन पुन: प्रस्तुत कर सकते हैं।

निष्पादन योजना को देखने से पता चलता है कि क्वेरी विश्लेषक स्पष्ट रूप से स्थिति को विभाजित करता है s_guid < NEWID() OR s_guid > NEWID():

क्वेरी योजना स्क्रीनशॉट

... जो पूरी तरह से समझाता है कि यह कभी-कभी विफल क्यों हो जाता है (यदि पहली उत्पन्न आईडी छोटी है और दी गई आईडी से दूसरी बड़ी है)।

एसक्यूएल सर्वर का मूल्यांकन करने के लिए अनुमति दी है A <> Bके रूप में A < B OR A > B, भले ही भाव से एक गैर नियतात्मक है? यदि हाँ, तो यह कहाँ प्रलेखित है? या हमें एक बग मिला?

दिलचस्प है, AND NOT (s_guid = NEWID())एक ही निष्पादन योजना (और एक ही यादृच्छिक परिणाम) प्राप्त करता है।

जब एक डेवलपर वैकल्पिक रूप से एक विशेष पंक्ति को बाहर करना चाहता था और इसका उपयोग करता था, तो हमें यह मुद्दा मिला:

s_guid <> ISNULL(@someParameter, NEWID())

"शॉर्टकट" के रूप में:

(@someParameter IS NULL OR s_guid <> @someParameter)

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


जवाबों:


22

एसक्यूएल सर्वर का मूल्यांकन करने के लिए अनुमति दी है A <> Bके रूप में A < B OR A > B, भले ही भाव से एक गैर नियतात्मक है?

यह कुछ हद तक विवादास्पद बिंदु है, और इसका उत्तर एक योग्य "हाँ" है।

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

इस मुद्दे के बहुत हिट करने के लिए हिट - अनुकूलन एक कार्यक्रम के शब्दार्थ को बदलने की अनुमति है? Ie: यदि कोई प्रोग्राम कुछ उत्तरों की पैदावार करता है, लेकिन धीरे-धीरे चलता है, तो क्या यह क्वेरी ऑप्टिमाइज़र के लिए वैध है कि वह प्रोग्राम तेजी से चलाए, फिर भी दिए गए परिणामों को बदल दें?

"नहीं!" चिल्लाने से पहले (मेरा अपना व्यक्तिगत झुकाव भी :-), विचार करें: अच्छी खबर यह है कि, 99% मामलों में, उत्तर समान हैं। इसलिए क्वेरी ऑप्टिमाइज़ेशन एक स्पष्ट जीत है। बुरी खबर यह है कि, यदि क्वेरी में साइड-इफ़ेक्टिंग कोड शामिल है, तो विभिन्न योजनाएँ वास्तव में अलग-अलग परिणाम दे सकती हैं। और NEWID () एक ऐसा साइड-इफ़ेक्टिंग (गैर-नियतात्मक) 'फ़ंक्शन' है जो अंतर को उजागर करता है। [वास्तव में, यदि आप प्रयोग करते हैं, तो आप दूसरों को समर्पित कर सकते हैं - उदाहरण के लिए, AND क्लॉज का शॉर्ट-सर्किट मूल्यांकन: दूसरा क्लॉज एक अंकगणितीय फूट डालो-शून्य को बनाओ - विभिन्न अनुकूलन कर सकते हैं निष्पादित करें कि दूसरा क्लॉज पहले क्लॉज से पहले हो] क्रेग की व्याख्या, इस धागे में कहीं और, कि स्क्लर ऑपरेटर्स द्वारा निष्पादित किए जाने पर SqlServer गारंटी नहीं देता है।

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

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

समय के साथ इस संबंध में व्यवहार के बदलाव का एक उदाहरण NULLIF गैर-नियतात्मक कार्यों जैसे कि RAND () के साथ गलत तरीके से काम करता है । वहाँ भी इसी तरह के अन्य मामलों में उपयोग कर रहे हैं जैसे COALESCEकि एक उपश्रेणी के साथ जो अप्रत्याशित परिणाम उत्पन्न कर सकते हैं, और जिन्हें धीरे-धीरे संबोधित भी किया जा रहा है।

जिम जारी है:

पुनर्चक्रण करना । । । मैंने देव टीम के साथ इस सवाल पर चर्चा की है। और अंततः हमने निम्नलिखित कारणों से वर्तमान व्यवहार को नहीं बदलने का फैसला किया है:

1) ऑप्टिमाइज़र स्केलर फ़ंक्शंस के निष्पादन की समय या संख्या की गारंटी नहीं देता है। यह एक लंबे समय से स्थाई कार्यकाल है। यह मौलिक 'लेवे' था, ऑप्टिमाइज़र को क्वेरी-प्लान निष्पादन में महत्वपूर्ण सुधार प्राप्त करने की पर्याप्त स्वतंत्रता देता है।

2) यह "एक बार प्रति-पंक्ति व्यवहार" एक नया मुद्दा नहीं है, हालांकि यह व्यापक रूप से चर्चा में नहीं है। हमने इसके व्यवहार को युकॉन रिलीज़ में वापस लाना शुरू कर दिया। लेकिन सभी मामलों में सटीक रूप से पिन करना काफी कठिन है, वास्तव में इसका क्या मतलब है! उदाहरण के लिए, क्या यह अंतिम परिणाम के लिए 'रास्ते में गणना की गई पंक्तियों को अंतरिम करने के लिए लागू होता है?' - किस मामले में यह स्पष्ट रूप से चुनी गई योजना पर निर्भर करता है। या यह केवल उन पंक्तियों पर लागू होता है जो अंततः पूर्ण परिणाम में दिखाई देंगे? - यहाँ एक बुरा पुनरावर्तन चल रहा है, जैसा कि मुझे यकीन है कि आप सहमत होंगे!

3) जैसा कि मैंने पहले उल्लेख किया है, हम "प्रदर्शन का अनुकूलन" करने के लिए डिफ़ॉल्ट हैं - जो कि 99% मामलों के लिए अच्छा है। 1% मामलों में, जहां परिणाम बदल सकते हैं - स्पॉट-इफ़ेक्टिंग 'फ़ंक्शंस' जैसे कि NEWID - और 'फ़िक्स' के लिए आसान (परिणाम के रूप में ट्रेडिंग पूर्ण) के लिए काफी आसान है। यह डिफ़ॉल्ट "प्रदर्शन को अनुकूलित करने" के लिए फिर से, लंबे समय से स्थापित है, और स्वीकार किया जाता है। (हां, यह पारंपरिक प्रोग्रामिंग भाषाओं के लिए कंपाइलरों द्वारा चुना गया रुख नहीं है, लेकिन ऐसा ही हो)।

तो, हमारी सिफारिशें हैं:

क) गैर-गारंटीकृत समय और संख्या-निष्पादन के अर्थ-विज्ञान पर निर्भरता से बचें। ख) टेबल के भावों में डीप न्यूड () का उपयोग करने से बचें। ग) एक विशेष व्यवहार (ट्रेडिंग पूर्ण) को लागू करने के लिए विकल्प का उपयोग करें

आशा है कि यह स्पष्टीकरण "बग को ठीक नहीं करेगा" के रूप में इस बग को बंद करने के हमारे कारणों को स्पष्ट करने में मदद करता है।


दिलचस्प है, AND NOT (s_guid = NEWID())एक ही निष्पादन योजना की पैदावार

यह सामान्यीकरण का परिणाम है, जो क्वेरी संकलन के दौरान बहुत पहले होता है। दोनों अभिव्यक्तियाँ बिल्कुल एक ही सामान्यीकृत रूप में संकलित होती हैं, इसलिए एक ही निष्पादन योजना निर्मित होती है।


इस मामले में, यदि हम किसी विशेष योजना को लागू करना चाहते हैं जो कि समस्या से बचने के लिए लगता है, तो हम (FORCESCAN) का उपयोग कर सकते हैं। निश्चित होने के लिए, हमें क्वेरी निष्पादित करने से पहले NEWID () के परिणाम को संग्रहीत करने के लिए एक चर का उपयोग करना चाहिए।
रज़वान सोकोल

11

यह यहाँ प्रलेखित (क्रमबद्ध) है:

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

उपयोगकर्ता-परिभाषित कार्य

यह एकमात्र क्वेरी फॉर्म नहीं है जहां क्वेरी प्लान NEWID () को कई बार निष्पादित करेगा और परिणाम को बदल देगा। यह भ्रामक है, लेकिन वास्तव में NEWID () के लिए महत्वपूर्ण है जो प्रमुख पीढ़ी और यादृच्छिक छँटाई के लिए उपयोगी है।

सबसे भ्रामक यह है कि सभी गैर-नियतात्मक कार्य वास्तव में इस तरह का व्यवहार नहीं करते हैं। उदाहरण के लिए RAND () और GETDATE () केवल एक बार क्वेरी के अनुसार निष्पादित होंगे।


क्या कोई ब्लॉग पोस्ट या ऐसा ही है जो बताता है कि क्यों / कब इंजन "नहीं के बराबर" को एक सीमा में बदल देगा?
मिस्टर मागू

3
मेरी जानकारी में नहीं। हो सकता है कि नियमित हो =, <और >एक ब्रीटी के खिलाफ कुशलतापूर्वक मूल्यांकन किया जा सकता है।
डेविड ब्राउन - Microsoft

5

इस लायक के लिए, यदि आप इस पुराने SQL 92 मानक दस्तावेज़ को देखते हैं , तो असमानता के आसपास की आवश्यकताओं को अनुभाग " 8.2 <comparison predicate>" में वर्णित किया गया है:

1) X और Y किसी भी दो संगत <पंक्ति मान निर्माता तत्व> s हैं। XV और YV को क्रमशः X और Y द्वारा दर्शाए गए मान हैं।

[...]

ii) "X <> Y" सत्य है यदि केवल और केवल XV और YV समान नहीं हैं।

[...]

7) Rx और Ry दो <पंक्ति मान कंस्ट्रक्टर> s की तुलना करें <predicate> और RXi और RYi क्रमशः Rx और Ry का i-th <रो वैल्यू कंस्ट्रक्टर एलिमेंट> होने दें। "Rx <Comp op> Ry" सत्य, असत्य, या अज्ञात इस प्रकार है:

[...]

b) "x <> Ry" सत्य है यदि केवल और केवल कुछ के लिए RXi <> RYi।

[...]

ज) "x <> Ry" झूठा है अगर और केवल अगर "Rx = Ry" सच है।

नोट: मैंने 7b और 7h को पूर्णता के लिए शामिल किया क्योंकि वे <>तुलना के बारे में बात करते हैं - मुझे नहीं लगता कि टी-एसक्यूएल में कई मूल्यों वाले पंक्ति मूल्य निर्माणकर्ताओं की तुलना तब तक लागू की जाती है, जब तक कि मैं सिर्फ बड़े पैमाने पर गलतफहमी नहीं रखता कि यह क्या कहता है - जो कि बहुत संभव है

यह भ्रमित करने वाला कचरा है। लेकिन अगर आप डंपस्टर डाइविंग रखना चाहते हैं ...

मुझे लगता है कि 1.ii वह आइटम है जो इस परिदृश्य में लागू होता है, क्योंकि हम "पंक्ति मूल्य निर्माता तत्वों" के मूल्यों की तुलना कर रहे हैं।

ii) "X <> Y" सत्य है यदि केवल और केवल XV और YV समान नहीं हैं।

मूल रूप से यह कह रहा X <> Yहै कि अगर X और Y द्वारा दर्शाए गए मूल्य समान नहीं हैं, तो यह सच है । चूँकि X < Y OR X > Yउस विधेय के लिए एक तार्किक रूप से समकक्ष फिर से लिखा गया है, यह आशावादी के लिए इसका उपयोग करने के लिए पूरी तरह से शांत है।

<>तुलनात्मक ऑपरेटर के दोनों ओर पंक्ति मूल्य निर्माण तत्वों के निर्धारण-नेस (या जो कुछ भी, आप इसे प्राप्त करते हैं) से संबंधित इस परिभाषा पर मानक कोई बाधा नहीं डालता है । इस तथ्य से निपटने के लिए उपयोगकर्ता कोड की जिम्मेदारी है कि एक तरफ एक मूल्य अभिव्यक्ति गैर-निर्धारक हो सकती है।


1
मैं मतदान (ऊपर या नीचे) से पुनर्विचार करूंगा लेकिन मैं आश्वस्त नहीं हूं। आपके द्वारा प्रदान किए जाने वाले उद्धरण "मूल्य" का उल्लेख करते हैं । मेरी समझ यह है कि तुलना दो मूल्यों के बीच है, प्रत्येक पक्ष पर एक। प्रत्येक पक्ष पर एक मूल्य के दो (या अधिक) तात्कालिकता के बीच नहीं। साथ ही, सभी गैर-नियतात्मक कार्यों में मानक (कम से कम 92 आप उद्धरण) का उल्लेख नहीं करते हैं। आपके समान तर्क से, हम मान सकते हैं कि मानक का अनुपालन करने वाला SQL उत्पाद कोई भी गैर-नियतात्मक कार्य नहीं करता है, बल्कि केवल मानक में उल्लिखित हैं।
ypercube y

@ प्रतिक्रिया के लिए धन्यवाद! मुझे लगता है कि आपकी व्याख्या निश्चित रूप से मान्य है। यह पहली बार है जब मैंने कभी उस डॉक्टर को पढ़ा है। यह "पंक्ति मूल्य निर्माता" द्वारा दर्शाए गए मूल्य के संदर्भ में मूल्यों का उल्लेख कर रहा है, जो कि डॉक्स में कहीं और कहता है कि यह एक स्केलर सबक्वेरी (कई अन्य चीजों के बीच) हो सकता है। विशेष रूप से स्केलर उपश्रेणी ऐसा लगता है कि यह गैर-नियतात्मक हो सकता है। लेकिन मैं वास्तव में नहीं जानता कि मैं किस बारे में बात कर रहा हूं =)
जोश डारनेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.