क्या SQL सर्वर सभी COALESCE फ़ंक्शन को पढ़ता है, भले ही पहला तर्क NULL न हो?


98

मैं एक टी-एसक्यूएल COALESCEफ़ंक्शन का उपयोग कर रहा हूं, जहां पहली बार चलने पर लगभग 95% पर पहला तर्क शून्य नहीं होगा। यदि पहला तर्क है NULL, तो दूसरा तर्क काफी लंबी प्रक्रिया है:

SELECT COALESCE(c.FirstName
                ,(SELECT TOP 1 b.FirstName
                  FROM TableA a 
                  JOIN TableB b ON .....)
                )

यदि, उदाहरण के लिए, c.FirstName = 'John'SQL सर्वर अभी भी उप-क्वेरी चलाएगा?

मुझे पता है कि VB.NET IIF()फ़ंक्शन के साथ, यदि दूसरा तर्क सही है, तो कोड अभी भी तीसरा तर्क पढ़ता है (भले ही इसका उपयोग नहीं किया जाएगा)।

जवाबों:


95

नहीं । यहाँ एक सरल परीक्षण है:

SELECT COALESCE(1, (SELECT 1/0)) -- runs fine
SELECT COALESCE(NULL, (SELECT 1/0)) -- throws error

यदि दूसरी स्थिति का मूल्यांकन किया जाता है, तो एक अपवाद को विभाजित-शून्य के लिए फेंक दिया जाता है।

प्रति MSDN प्रलेखन इस के लिए कैसे संबंधित है COALESCEदुभाषिया द्वारा देखा जाता है - यह सिर्फ एक आसान तरीका एक लिखने के लिए है CASEबयान।

CASE SQL सर्वर में केवल एक फ़ंक्शन के लिए जाना जाता है जो (ज्यादातर) मज़बूती से शॉर्ट सर्किट होता है।

स्केलर चर और समुच्चय की तुलना में कुछ अपवाद हैं जब हारून बर्ट्रेंड द्वारा यहां एक अन्य उत्तर में दिखाया गया है (और यह दोनों CASEऔर पर लागू होगा COALESCE):

DECLARE @i INT = 1;
SELECT CASE WHEN @i = 1 THEN 1 ELSE MIN(1/0) END;

शून्य त्रुटि से एक विभाजन उत्पन्न करेगा।

इसे एक बग माना जाना चाहिए, और एक नियम के रूप में COALESCEबाएं से दाएं तक पार्स होगा।


6
@ जेएनके कृपया एक बहुत ही सरल मामला देखने के लिए मेरा उत्तर देखें जहां यह सच नहीं है (मेरी चिंता यह है कि अभी और भी हैं, फिर भी अनदेखा परिदृश्य - यह सहमत होना मुश्किल है कि CASEहमेशा बाएं से दाएं और हमेशा शॉर्ट सर्किट का मूल्यांकन करें )।
हारून बर्ट्रेंड

4
अन्य रोचक व्यवहार @ SQLKiwi ने मुझे बताया: SELECT COALESCE((SELECT CASE WHEN RAND() <= 0.5 THEN 1 END), 1);- कई बार दोहराएं। आपको NULLकभी-कभी मिलेगा । के साथ फिर से कोशिश करें ISNULL- आपको कभी नहीं मिलेगा NULL...
हारून बर्ट्रेंड


@ मर्टिन हां मैं ऐसा मानता हूं। लेकिन ऐसा व्यवहार नहीं है कि अधिकांश उपयोगकर्ता तब तक सहज महसूस करते हैं जब तक कि उन्होंने उस मुद्दे के बारे में नहीं सुना (या काट दिया गया हो)।
हारून बर्ट्रेंड

73

कैसे इस बारे में - जैसा कि इत्ज़िक बेन-गण द्वारा मुझे बताया गया है, जिसे इसके बारे में जैम लाफार्ज ने बताया था ?

DECLARE @i INT = 1;
SELECT CASE WHEN @i = 1 THEN 1 ELSE MIN(1/0) END;

नतीजा:

Msg 8134, Level 16, State 1, Line 2
Divide by zero error encountered.

निश्चित रूप से तुच्छ वर्कअराउंड हैं, लेकिन अभी भी बिंदु यह है कि हमेशा बाएं से दाएं मूल्यांकन / शॉर्ट-सर्कुलेटिंग की गारंटी CASEनहीं देता है। मैंने यहां बग की सूचना दी और इसे "डिजाइन द्वारा" बंद कर दिया गया। पॉल व्हाइट ने बाद में इस कनेक्ट आइटम को दायर किया , और इसे फिक्स्ड के रूप में बंद कर दिया गया। इसलिए नहीं कि यह प्रति सेक्‍यूट तय किया गया था, बल्कि इसलिए कि उन्‍होंने बुक्‍स ऑनलाइन को उस परिदृश्‍य के अधिक सटीक विवरण के साथ अपडेट किया, जहां एग्रीगेट किसी एक्‍सप्रेशन के मूल्‍यांकन क्रम को बदल सकते हैं । मैंने हाल ही में यहाँ इस बारे में और अधिक ब्लॉग किया हैCASE

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

यद्यपि आप आमतौर पर CASEबाएं से दाएं और शॉर्ट-सर्किट का मूल्यांकन करने के लिए भरोसा कर सकते हैं , जैसा कि प्रलेखन में वर्णित है, यह कहना सही नहीं है कि आप हमेशा ऐसा कर सकते हैं। इस पृष्ठ पर दो प्रदर्शित मामले हैं जहां यह सत्य नहीं है, और न ही बग किसी भी SQL सर्वर के सार्वजनिक रूप से उपलब्ध संस्करण में तय किया गया है।

यहां EDIT एक और मामला है (मुझे ऐसा करने से रोकने की जरूरत है) जहां कोई CASEअभिव्यक्ति उस क्रम में मूल्यांकन नहीं करती है जिसकी आप उम्मीद करते हैं, भले ही कोई भी एग्रीगेट शामिल न हो।


2
और ऐसा लग रहा है कि इसके साथ एक और मुद्दा CASE था जो चुपचाप तय किया गया था
मार्टिन स्मिथ

IMO यह साबित नहीं करता है कि CASE अभिव्यक्ति के मूल्यांकन की गारंटी नहीं है क्योंकि समग्र मूल्यों की गणना चयन से पहले की जाती है (ताकि उनका उपयोग अंदर किया जा सके)।
सलमान ए

1
@SalmanA मुझे यकीन नहीं है कि यह और क्या करता है सिवाय इसके कि वास्तव में एक CASE अभिव्यक्ति में मूल्यांकन के आदेश की गारंटी नहीं है। हमें एक अपवाद मिल रहा है क्योंकि कुल गणना पहले की जाती है, भले ही वह एक ईएलएसई खंड में है - यदि आप प्रलेखन द्वारा जाते हैं - कभी भी नहीं पहुंचना चाहिए।
हारून बर्ट्रेंड

@AaronBertrand समुच्चय की गणना CASE कथन से पहले की जाती है (और उन्हें IMO होना चाहिए)। संशोधित प्रलेखन वास्तव में यह इंगित करता है, कि CASE के मूल्यांकन से पहले त्रुटि होती है।
सलमान ए

@ साल्मन यह अब भी कैजुअल डेवलपर को प्रदर्शित करता है कि CASE अभिव्यक्ति का मूल्यांकन उस क्रम में नहीं किया गया है जिसमें लिखा गया था - अंतर्निहित यांत्रिकी अप्रासंगिक हैं यदि आप सभी यह करने की कोशिश कर रहे हैं कि यह समझने की कोशिश की जा रही है कि एक त्रुटि एक CASE शाखा से आ रही है जो कि नहीं होनी चाहिए ' टी पहुंच गए हैं। क्या आपके पास इस पृष्ठ पर अन्य सभी उदाहरणों के खिलाफ भी तर्क हैं?
हारून बर्ट्रेंड

37

इस पर मेरा विचार यह है कि प्रलेखन यह स्पष्ट रूप से स्पष्ट करता है कि इरादा यह है कि CASE को शॉर्ट-सर्किट करना चाहिए। जैसा कि हारून का उल्लेख है, ऐसे कई मामले (हा!) हैं जहाँ यह हमेशा सच नहीं दिखाया गया है।

अब तक, इन सभी को कीड़े के रूप में स्वीकार किया गया है और तय किया गया है - हालांकि जरूरी नहीं कि SQL सर्वर के एक संस्करण में आप आज खरीद सकते हैं और पैच कर सकते हैं (निरंतर-तह बग ने अभी तक इसे संचयी अद्यतन AFAIK में नहीं बनाया है)। मूल रूप से इटज़िक बेन-गण द्वारा रिपोर्ट की गई नवीनतम संभावित बग - अभी तक जांच की जानी है (या तो आरोन या मैं इसे शीघ्र ही कनेक्ट करने के लिए जोड़ दूंगा)।

मूल प्रश्न से संबंधित, CASE (और इसलिए COALESCE) के साथ अन्य समस्याएँ हैं जहाँ साइड-इफ़ेक्टिंग फ़ंक्शंस या सब-क्वेरीज़ का उपयोग किया जाता है। विचार करें:

SELECT COALESCE((SELECT CASE WHEN RAND() <= 0.5 THEN 999 END), 999);
SELECT ISNULL((SELECT CASE WHEN RAND() <= 0.5 THEN 999 END), 999);

COALESCE फ़ॉर्म अक्सर NULL, https://connect.microsoft.com/SQLServer/feedback/details/546437/coalesce-subquery-1-may-return-null पर अधिक विवरण देता है

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

संक्षेप में, मुझे लगता है कि आप उचित रूप से आश्वस्त हो सकते हैं कि CASE सामान्य रूप से शॉर्ट-सर्किट करेगा (विशेष रूप से यदि एक उचित-कुशल व्यक्ति निष्पादन योजना का निरीक्षण करता है, और यह निष्पादन योजना एक योजना गाइड या संकेत के साथ 'लागू' है) लेकिन अगर आपको ज़रूरत है एक निरपेक्ष गारंटी, आपको एसक्यूएल लिखना होगा जिसमें अभिव्यक्ति शामिल नहीं है।

मेरे हिसाब से बहुत संतोषजनक स्थिति नहीं है।


18

मैं एक मामले में जहां का सामना करना पड़ा CASE/ COALESCEनहीं शॉर्ट सर्किट है। निम्न TVF 1पैरामीटर के रूप में पारित होने पर PK उल्लंघन बढ़ाएगा ।

CREATE FUNCTION F (@P INT)
RETURNS @T TABLE (
  C INT PRIMARY KEY)
AS
  BEGIN
      INSERT INTO @T
      VALUES      (1),
                  (@P)

      RETURN
  END

अगर इस प्रकार कहा जाता है

DECLARE @Number INT = 1

SELECT COALESCE(@Number, (SELECT number
                          FROM   master..spt_values
                          WHERE  type = 'P'
                                 AND number = @Number), 
                         (SELECT TOP (1)  C
                          FROM   F(@Number))) 

या के रूप में

DECLARE @Number INT = 1

SELECT CASE
         WHEN @Number = 1 THEN @Number
         ELSE (SELECT TOP (1) C
               FROM   F(@Number))
       END 

दोनों परिणाम देते हैं

प्राथमिक कुंजी बाधा 'PK__F__3BD019A800551192' का उल्लंघन। डुप्लिकेट कुंजी को ऑब्जेक्ट 'dbo। @ T' में सम्मिलित नहीं किया जा सकता है। डुप्लिकेट कुंजी मान (1) है।

यह दिखाते हुए कि SELECT(या कम से कम तालिका चर आबादी) अभी भी बाहर किया जाता है और एक त्रुटि उठाता है, भले ही कथन की उस शाखा तक कभी नहीं पहुंचा जाए। COALESCEसंस्करण के लिए योजना नीचे है।

योजना

क्वेरी का यह पुनर्लेखन समस्या से बचने के लिए प्रकट होता है

SELECT COALESCE(Number, (SELECT number
                          FROM   master..spt_values
                          WHERE  type = 'P'
                                 AND number = Number), 
                         (SELECT TOP (1)  C
                          FROM   F(Number))) 
FROM (VALUES(1)) V(Number)   

जो योजना देता है

Plan2


8

एक और उदाहरण

CREATE TABLE T1 (C INT PRIMARY KEY)

CREATE TABLE T2 (C INT PRIMARY KEY)

INSERT INTO T1 
OUTPUT inserted.* INTO T2
VALUES (1),(2),(3);

पूछताछ

SET STATISTICS IO ON;

SELECT T1.C,
       COALESCE(T1.C , CASE WHEN EXISTS (SELECT * FROM T2 WHERE T2.C = T1.C)  THEN -1 END)
FROM T1
OPTION (LOOP JOIN)

शो के खिलाफ T2बिल्कुल नहीं पढ़ता है ।

की तलाश T2विधेय के माध्यम से गुजरती है और ऑपरेटर को कभी भी निष्पादित नहीं किया जाता है। परंतु

SELECT T1.C,
       COALESCE(T1.C , CASE WHEN EXISTS (SELECT * FROM T2 WHERE T2.C = T1.C)  THEN -1 END)
FROM T1
OPTION (MERGE JOIN)

क्या बताते हैं कि T2पढ़ा जाता है। हालांकि इससे कोई मूल्य T2वास्तव में कभी नहीं चाहिए।

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


7

मैं सिर्फ एक रणनीति का उल्लेख करना चाहता था जिसे आपने नहीं माना होगा। यह यहां मैच नहीं हो सकता है, लेकिन यह कभी-कभी काम आता है। देखें कि क्या यह संशोधन आपको कोई बेहतर प्रदर्शन देता है:

SELECT COALESCE(c.FirstName
            ,(SELECT TOP 1 b.FirstName
              FROM TableA a 
              JOIN TableB b ON .....
              WHERE C.FirstName IS NULL) -- this is the changed part
            )

ऐसा करने का एक और तरीका यह हो सकता है (मूल रूप से समतुल्य, लेकिन यदि आवश्यक हो तो आप अन्य क्वेरी से अधिक कॉलम एक्सेस कर सकते हैं):

SELECT COALESCE(c.FirstName, x.FirstName)
FROM
   TableC c
   OUTER APPLY (
      SELECT TOP 1 b.FirstName
      FROM
         TableA a 
         JOIN TableB b ON ...
      WHERE
         c.FirstName IS NULL -- the important part
   ) x

मूल रूप से यह "हार्ड" ज्वाइनिंग टेबल्स की एक तकनीक है, लेकिन इस शर्त पर कि जब किसी भी पंक्ति को जोइन किया जाना चाहिए। मेरे अनुभव में इसने कई बार निष्पादन योजनाओं की मदद की है।


3

नहीं, ऐसा नहीं होगा। यह केवल चल पाएंगे जब c.FirstNameहै NULL

हालांकि, आपको इसे स्वयं प्रयास करना चाहिए। प्रयोग। आपने कहा कि आपकी उपश्रेणी लम्बी है। आकलन के लिये केंद्रीय बिंदु। इस पर अपने निष्कर्ष निकालें।

@ सब-क्वेरी रन किए जाने पर उत्तर अधिक पूर्ण है।

हालाँकि, मुझे अभी भी लगता है कि आपको अपनी क्वेरी और उपयोग को फिर से करना चाहिए LEFT JOIN। अधिकांश समय, उप क्वेरी को LEFT JOINs का उपयोग करने के लिए आपकी क्वेरी को पुनः लागू करके हटाया जा सकता है ।

उप प्रश्नों का उपयोग करने में समस्या यह है कि आपका समग्र विवरण धीमी गति से चलेगा क्योंकि उप क्वेरी को मुख्य क्वेरी के परिणाम सेट में प्रत्येक पंक्ति के लिए चलाया जाता है।


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

3

वास्तविक मानक का कहना है कि संपूर्ण डेटा के प्रकार को निर्धारित करने के लिए WHEN के सभी खंड (साथ ही साथ ELSE खंड) को पार्स किया जाना है। मुझे वास्तव में यह निर्धारित करने के लिए अपने पुराने नोटों में से कुछ को निकालना होगा कि कैसे एक त्रुटि को संभाला जाए। लेकिन सिर्फ हाथ से, 1/0 पूर्णांक का उपयोग करता है, इसलिए मुझे लगता है कि यह एक त्रुटि है। यह पूर्णांक डेटा प्रकार के साथ एक त्रुटि है। जब आपके पास केवल मोटे सूची में नल होते हैं, तो डेटा प्रकार निर्धारित करने के लिए यह थोड़ा मुश्किल है, और यह एक और समस्या है।

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