Database
 sql >> डेटाबेस >  >> RDS >> Database

केस एक्सप्रेशन के डर्टी सीक्रेट्स

CASE अभिव्यक्ति टी-एसक्यूएल में मेरी पसंदीदा संरचनाओं में से एक है। यह काफी लचीला है, और कभी-कभी उस क्रम को नियंत्रित करने का एकमात्र तरीका है जिसमें SQL सर्वर विधेय का मूल्यांकन करेगा।
हालांकि, इसे अक्सर गलत समझा जाता है।

टी-एसक्यूएल केस एक्सप्रेशन क्या है?

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

T-SQL में CASE के दो रूप हैं:

  • सरल केस एक्सप्रेशन - जब आपको केवल समानता का मूल्यांकन करने की आवश्यकता हो:

    CASE <input> WHEN <eval> THEN <return> … [ELSE <return>] END

  • केस एक्सप्रेशन खोजा गया - जब आपको अधिक जटिल अभिव्यक्तियों का मूल्यांकन करने की आवश्यकता हो, जैसे कि असमानता, LIKE, या IS NOT NULL:

    CASE WHEN <input_bool> THEN <return> … [ELSE <return>] END

रिटर्न एक्सप्रेशन हमेशा एक ही मान होता है, और आउटपुट डेटा प्रकार डेटा प्रकार वरीयता द्वारा निर्धारित किया जाता है।

जैसा कि मैंने कहा, CASE अभिव्यक्ति को अक्सर गलत समझा जाता है; यहां कुछ उदाहरण दिए गए हैं:

CASE एक एक्सप्रेशन है, स्टेटमेंट नहीं

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

SELECT CASE [status]
    WHEN 'A' THEN
        StatusLabel      = 'Authorized',
        LastEvent        = AuthorizedTime
    WHEN 'C' THEN
        StatusLabel      = 'Completed',
        LastEvent        = CompletedTime
    END
FROM dbo.some_table;

या यह:

SELECT CASE WHEN @foo = 1 THEN
    (SELECT foo, bar FROM dbo.fizzbuzz)
ELSE
    (SELECT blat, mort FROM dbo.splunge)
END;

इस प्रकार का प्रवाह नियंत्रण तर्क CASE . के साथ संभव हो सकता है बयान अन्य भाषाओं में (जैसे VBScript), लेकिन Transact-SQL के CASE . में नहीं अभिव्यक्ति . CASE का उपयोग करने के लिए उसी क्वेरी लॉजिक में, आपको CASE . का उपयोग करना होगा अभिव्यक्ति प्रत्येक आउटपुट कॉलम के लिए:

SELECT 
  StatusLabel = CASE [status]
      WHEN 'A' THEN 'Authorized' 
      WHEN 'C' THEN 'Completed' END,
  LastEvent = CASE [status]
      WHEN 'A' THEN AuthorizedTime
      WHEN 'C' THEN CompletedTime END
FROM dbo.some_table;

CASE हमेशा शॉर्ट सर्किट नहीं करेगा

आधिकारिक दस्तावेज में एक बार यह निहित किया गया था कि पूरी अभिव्यक्ति शॉर्ट-सर्किट होगी, जिसका अर्थ है कि यह बाएं से दाएं अभिव्यक्ति का मूल्यांकन करेगा, और मैच हिट होने पर मूल्यांकन करना बंद कर देगा:

CASE स्टेटमेंट [sic!] अपनी शर्तों का क्रमिक रूप से मूल्यांकन करता है और पहली शर्त के साथ रुकता है जिसकी स्थिति संतुष्ट होती है।

हालाँकि, यह हमेशा सच नहीं होता है। और इसके श्रेय के लिए, एक अधिक वर्तमान संस्करण में, पृष्ठ ने एक ऐसे परिदृश्य की व्याख्या करने का प्रयास किया जहां इसकी गारंटी नहीं है। लेकिन इसे केवल कहानी का हिस्सा मिलता है:

कुछ स्थितियों में, CASE स्टेटमेंट [sic!] के इनपुट के रूप में एक्सप्रेशन के परिणाम प्राप्त करने से पहले एक एक्सप्रेशन का मूल्यांकन किया जाता है। इन भावों के मूल्यांकन में त्रुटियाँ संभव हैं। जब किसी CASE कथन [sic!] के तर्कों में प्रकट होने वाले समग्र भावों का मूल्यांकन पहले किया जाता है, फिर CASE कथन [sic!] को प्रदान किया जाता है। उदाहरण के लिए, MAX एग्रीगेट का मान उत्पन्न करते समय निम्न क्वेरी शून्य त्रुटि से विभाजित करती है। यह CASE व्यंजक का मूल्यांकन करने से पहले होता है।

शून्य उदाहरण से विभाजित करना बहुत आसान है, और मैंने इसे dba.stackexchange.com पर इस उत्तर में प्रदर्शित किया:

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

परिणाम:

संदेश 8134, स्तर 16, राज्य 1
शून्य त्रुटि से विभाजित करें।

छोटे-छोटे उपाय हैं (जैसे ELSE (SELECT MIN(1/0)) END ), लेकिन यह कई लोगों के लिए एक वास्तविक आश्चर्य के रूप में आता है जिन्होंने बुक्स ऑनलाइन से उपरोक्त वाक्यों को याद नहीं किया है। इट्ज़िक बेन-गण (@ItzikBenGan) द्वारा एक निजी ई-मेल वितरण सूची पर बातचीत में मुझे पहली बार इस विशिष्ट परिदृश्य से अवगत कराया गया था, जिसे शुरू में Jaime Lafargue द्वारा अधिसूचित किया गया था। मैंने Connect #690017 में बग की सूचना दी:CASE / COALESCE हमेशा पाठ्य क्रम में मूल्यांकन नहीं करेगा; इसे तेजी से "डिजाइन द्वारा" के रूप में बंद कर दिया गया था। पॉल व्हाइट (ब्लॉग | @SQL_Kiwi) ने बाद में Connect #691535 :Aggregates Don't Follow the Semantics Of CASE दायर किया, और इसे "फिक्स्ड" के रूप में बंद कर दिया गया। इस मामले में सुधार, पुस्तकें ऑनलाइन लेख में स्पष्टीकरण था; अर्थात्, वह स्निपेट जिसे मैंने ऊपर कॉपी किया था।

यह व्यवहार खुद को कुछ अन्य, कम स्पष्ट परिदृश्यों में भी उत्पन्न कर सकता है। उदाहरण के लिए, Connect #780132 :FREETEXT() CASE स्टेटमेंट में मूल्यांकन के आदेश का सम्मान नहीं करता है (कोई एग्रीगेट शामिल नहीं है) यह दर्शाता है कि, ठीक है, CASE कुछ पूर्ण-पाठ कार्यों का उपयोग करते समय मूल्यांकन आदेश बाएं से दाएं होने की गारंटी नहीं है। उस आइटम पर, पॉल व्हाइट ने टिप्पणी की कि उन्होंने नए LAG() . का उपयोग करके भी कुछ ऐसा ही देखा है SQL सर्वर 2012 में पेश किया गया फ़ंक्शन। मेरे पास रेप्रो आसान नहीं है, लेकिन मैं उस पर विश्वास करता हूं, और मुझे नहीं लगता कि हमने सभी किनारे के मामलों का पता लगाया है जहां यह हो सकता है।

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

RAND() का मूल्यांकन एक से अधिक बार किया जा सकता है

मैंने अक्सर लोगों को सरल . लिखते हुए देखा है CASE अभिव्यक्ति, इस तरह:

SELECT CASE @variable 
  WHEN 1 THEN 'foo'
  WHEN 2 THEN 'bar'
END

यह समझना महत्वपूर्ण है कि इसे खोज . के रूप में निष्पादित किया जाएगा CASE अभिव्यक्ति, इस तरह:

SELECT CASE  
  WHEN @variable = 1 THEN 'foo'
  WHEN @variable = 2 THEN 'bar'
END

यह समझना महत्वपूर्ण है कि मूल्यांकन की जा रही अभिव्यक्ति का मूल्यांकन कई बार किया जाएगा, क्योंकि यह वास्तव में मूल्यांकन हो सकता है कई बार। जब यह एक चर, या एक स्थिर, या एक स्तंभ संदर्भ है, तो यह एक वास्तविक समस्या होने की संभावना नहीं है; हालांकि, चीजें तेजी से बदल सकती हैं जब यह एक गैर-नियतात्मक कार्य है। विचार करें कि यह अभिव्यक्ति एक SMALLINT उत्पन्न करती है 1 और 3 के बीच; आगे बढ़ें और इसे कई बार चलाएं, और आपको हमेशा उन तीन मानों में से एक मिलेगा:

SELECT CONVERT(SMALLINT, 1+RAND()*3);

अब, इसे एक साधारण CASE में डालें अभिव्यक्ति, और इसे एक दर्जन बार चलाएं - अंततः आपको NULL . का परिणाम मिलेगा :

SELECT [result] = CASE CONVERT(SMALLINT, 1+RAND()*3)
  WHEN 1 THEN 'one'
  WHEN 2 THEN 'two'
  WHEN 3 THEN 'three'
END;

यह कैसे होता है? खैर, पूरा CASE एक्सप्रेशन को खोजे गए एक्सप्रेशन तक विस्तृत किया जाता है, जो इस प्रकार है:

SELECT [result] = CASE 
  WHEN CONVERT(SMALLINT, 1+RAND()*3) = 1 THEN 'one'
  WHEN CONVERT(SMALLINT, 1+RAND()*3) = 2 THEN 'two'
  WHEN CONVERT(SMALLINT, 1+RAND()*3) = 3 THEN 'three'
  ELSE NULL -- this is always implicitly there
END;

बदले में, क्या होता है कि प्रत्येक WHEN क्लॉज RAND() का मूल्यांकन और आह्वान करता है स्वतंत्र रूप से - और प्रत्येक मामले में यह एक अलग मूल्य प्राप्त कर सकता है। मान लें कि हम व्यंजक दर्ज करते हैं, और हम पहले WHEN . की जांच करते हैं खंड, और परिणाम 3 है; हम उस खंड को छोड़ देते हैं और आगे बढ़ते हैं। यह बोधगम्य है कि RAND() . होने पर अगले दो खंड दोनों 1 लौटाएंगे फिर से मूल्यांकन किया जाता है - जिस स्थिति में किसी भी स्थिति का मूल्यांकन सही नहीं किया जाता है, इसलिए ELSE लेता है।

अन्य अभिव्यक्तियों का मूल्यांकन एक से अधिक बार किया जा सकता है

यह समस्या RAND() . तक सीमित नहीं है समारोह। इन गतिमान लक्ष्यों से आने वाली गैर-नियतात्मकता की उसी शैली की कल्पना करें:

SELECT 
  [crypt_gen]   = 1+ABS(CRYPT_GEN_RANDOM(10) % 20),
  [newid]       = LEFT(NEWID(),2),
  [checksum]    = ABS(CHECKSUM(NEWID())%3);

यदि कई बार मूल्यांकन किया जाए तो ये भाव स्पष्ट रूप से एक अलग मूल्य प्राप्त कर सकते हैं। और एक खोजे गए CASE . के साथ अभिव्यक्ति, ऐसे समय होंगे जब प्रत्येक पुनर्मूल्यांकन वर्तमान WHEN के लिए विशिष्ट खोज से बाहर हो जाएगा , और अंत में ELSE . दबाएं खंड। इससे खुद को बचाने के लिए, एक विकल्प यह है कि आप अपने खुद के स्पष्ट ELSE . को हमेशा हार्ड-कोड करें; केवल उस फ़ॉलबैक मान के बारे में सावधान रहें जिसे आप वापस करना चाहते हैं, क्योंकि यदि आप समान वितरण की तलाश में हैं तो इसका कुछ तिरछा प्रभाव होगा। एक अन्य विकल्प केवल अंतिम WHEN . को बदलना है ELSE . का खंड , लेकिन यह अभी भी असमान वितरण को बढ़ावा देगा। पसंदीदा विकल्प, मेरी राय में, एक बार स्थिति का मूल्यांकन करने के लिए SQL सर्वर को आज़माना और मजबूर करना है (हालाँकि यह एक ही क्वेरी के भीतर हमेशा संभव नहीं होता है)। उदाहरण के लिए, इन दो परिणामों की तुलना करें:

-- Query A: expression referenced directly in CASE; no ELSE:
SELECT x, COUNT(*) FROM
(
  SELECT x = CASE ABS(CHECKSUM(NEWID())%3) 
  WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' END 
  FROM sys.all_columns
) AS y GROUP BY x;
 
-- Query B: additional ELSE clause:
SELECT x, COUNT(*) FROM
(
  SELECT x = CASE ABS(CHECKSUM(NEWID())%3) 
  WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' ELSE '2' END 
  FROM sys.all_columns
) AS y GROUP BY x;
 
-- Query C: Final WHEN converted to ELSE:
SELECT x, COUNT(*) FROM
(
  SELECT x = CASE ABS(CHECKSUM(NEWID())%3) 
  WHEN 0 THEN '0' WHEN 1 THEN '1' ELSE '2' END 
  FROM sys.all_columns
) AS y GROUP BY x;
 
-- Query D: Push evaluation of NEWID() to subquery:
SELECT x, COUNT(*) FROM
(
  SELECT x = CASE x WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' END 
  FROM 
  (
    SELECT x = ABS(CHECKSUM(NEWID())%3) FROM sys.all_columns
  ) AS x
) AS y GROUP BY x;

वितरण:

<थ>प्रश्न ए <थ>प्रश्न बी <थ>प्रश्न सी <थ>क्वेरी डी
मान
शून्य 2,572
0 2,923 2,900 2,928 2,949
1 1,946 1,959 1,927 2,896
2 1,295 3,877 3,881 2,891

विभिन्न क्वेरी तकनीकों के साथ मानों का वितरण

इस मामले में, मैं इस तथ्य पर भरोसा कर रहा हूं कि SQL सर्वर ने सबक्वायरी में अभिव्यक्ति का मूल्यांकन करने के लिए चुना है और इसे खोजे गए CASE में पेश नहीं किया है। अभिव्यक्ति, लेकिन यह केवल यह प्रदर्शित करने के लिए है कि वितरण को और भी अधिक करने के लिए मजबूर किया जा सकता है। वास्तव में, यह हमेशा अनुकूलक द्वारा चुना गया विकल्प नहीं हो सकता है, इसलिए कृपया इस छोटी सी चाल से न सीखें। :-)

CHOOSE() भी प्रभावित होता है

आप देखेंगे कि यदि आप CHECKSUM(NEWID()) . को प्रतिस्थापित करते हैं RAND() . के साथ एक्सप्रेशन अभिव्यक्ति, आपको पूरी तरह से अलग परिणाम मिलेंगे; सबसे विशेष रूप से, बाद वाला केवल एक मान लौटाएगा। ऐसा इसलिए है क्योंकि RAND() , जैसे GETDATE() और कुछ अन्य अंतर्निहित कार्यों को रनटाइम स्थिरांक के रूप में विशेष उपचार दिया जाता है, और केवल एक बार प्रति संदर्भ का मूल्यांकन किया जाता है पूरी पंक्ति के लिए। ध्यान दें कि यह अभी भी NULL लौटा सकता है पिछले कोड नमूने में पहली क्वेरी की तरह।

यह समस्या भी CASE . तक ही सीमित नहीं है अभिव्यक्ति; आप अन्य अंतर्निहित कार्यों के साथ समान व्यवहार देख सकते हैं जो समान अंतर्निहित शब्दार्थ का उपयोग करते हैं। उदाहरण के लिए, CHOOSE अधिक विस्तृत खोज के लिए केवल वाक्यात्मक चीनी है CASE अभिव्यक्ति, और इससे NULL भी प्राप्त होगा कभी-कभी:

SELECT [choose] = CHOOSE(CONVERT(SMALLINT, 1+RAND()*3),'one','two','three');

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

COALESCE() भी प्रभावित होता है

अंत में, हमें यह जांचना चाहिए कि COALESCE समान मुद्दे हो सकते हैं। आइए मान लें कि ये भाव समान हैं:

SELECT COALESCE(@variable, 'constant');
 
SELECT CASE WHEN @variable IS NOT NULL THEN @variable ELSE 'constant' END);

इस मामले में, @variable दो बार मूल्यांकन किया जाएगा (जैसा कि इस कनेक्ट आइटम में वर्णित कोई फ़ंक्शन या सबक्वेरी होगा)।

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

SELECT COALESCE(NULLIF(CONVERT(SMALLINT,1+RAND()*5),3),-1);

(अंग्रेज़ी में, अंदर से बाहर काम करना:अभिव्यक्ति के परिणाम को रूपांतरित करें 1+RAND()*5 एक छोटे से करने के लिए; यदि उस रूपांतरण का परिणाम 3 है, तो उसे NULL . पर सेट करें; यदि उसका परिणाम NULL . है , इसे -1 पर सेट करें। आप इसे और अधिक क्रिया के साथ लिख सकते हैं CASE अभिव्यक्ति, लेकिन संक्षेप में राजा लगता है।)

यदि आप इसे कई बार चलाते हैं, तो आपको 1-5 के साथ-साथ -1 के मानों की श्रेणी देखनी चाहिए। आप 3 के कुछ उदाहरण देखेंगे, और आपने यह भी देखा होगा कि आप कभी-कभी NULL देखते हैं , हालांकि आप इनमें से किसी भी परिणाम की अपेक्षा नहीं कर सकते हैं। आइए वितरण की जांच करें:

USE tempdb;
GO
CREATE TABLE dbo.dist(TheNumber SMALLINT);
GO
INSERT dbo.dist(TheNumber) SELECT COALESCE(NULLIF(CONVERT(SMALLINT,1+RAND()*5),3),-1);
GO 10000
SELECT TheNumber, occurences = COUNT(*) FROM dbo.dist 
  GROUP BY TheNumber ORDER BY TheNumber;
GO
DROP TABLE dbo.dist;

परिणाम (आपके परिणाम निश्चित रूप से भिन्न होंगे, लेकिन मूल प्रवृत्ति समान होनी चाहिए):

संख्या घटनाएं
NULL 1,654
-1 2,002
1 1,290
2 1,266
3 1,287
4 1,251
5 1,250

COALESCE का उपयोग करके TheNumber का वितरण

किसी खोजे गए CASE एक्सप्रेशन को तोड़ना

क्या आप अभी तक अपना सिर खुजला रहे हैं? मान NULL . कैसे करते हैं और 3 दिखाई देते हैं, और NULL . के लिए वितरण क्यों है? और -1 काफी अधिक? ठीक है, मैं सीधे पूर्व का उत्तर दूंगा, और बाद के लिए परिकल्पनाओं को आमंत्रित करूंगा।

RAND() . के बाद से, व्यंजक मोटे तौर पर तार्किक रूप से निम्नलिखित तक विस्तृत होता है NULLIF . के अंदर दो बार मूल्यांकन किया जाता है , और उसके बाद COALESCE . की प्रत्येक शाखा के लिए दो मूल्यांकनों से गुणा करें समारोह। मेरे पास डीबगर आसान नहीं है, इसलिए यह जरूरी नहीं है कि *बिल्कुल* SQL सर्वर के अंदर क्या किया जाता है, लेकिन यह बिंदु को समझाने के लिए पर्याप्त समकक्ष होना चाहिए:

SELECT 
  CASE WHEN 
      CASE WHEN CONVERT(SMALLINT,1+RAND()*5) = 3 THEN NULL 
      ELSE CONVERT(SMALLINT,1+RAND()*5) 
      END 
    IS NOT NULL 
    THEN
      CASE WHEN CONVERT(SMALLINT,1+RAND()*5) = 3 THEN NULL 
      ELSE CONVERT(SMALLINT,1+RAND()*5) 
      END
    ELSE -1
  END
END

तो आप देख सकते हैं कि कई बार मूल्यांकन किए जाने पर शीघ्र ही अपनी खुद की साहसिक ™ पुस्तक चुनें, और कैसे दोनों NULL और 3 संभावित परिणाम हैं जो मूल कथन की जांच करते समय संभव नहीं लगते हैं। एक दिलचस्प पक्ष नोट:यदि आप उपरोक्त वितरण स्क्रिप्ट को लेते हैं और COALESCE को प्रतिस्थापित करते हैं तो ऐसा बिल्कुल नहीं होता है ISNULL . के साथ . उस स्थिति में, NULL . की कोई संभावना नहीं है आउटपुट; वितरण मोटे तौर पर इस प्रकार है:

संख्या घटनाएं
-1 1,966
1 1,585
2 1,644
3 1,573
4 1,598
5 1,634

ISNULL का उपयोग करके TheNumber का वितरण

फिर, आपके वास्तविक परिणाम निश्चित रूप से भिन्न होंगे, लेकिन बहुत अधिक नहीं होने चाहिए। मुद्दा यह है कि हम अभी भी देख सकते हैं कि 3 अक्सर दरारों से गुजरते हैं, लेकिन ISNULL NULL . की संभावना को जादुई रूप से समाप्त कर देता है इसे पूरा करने के लिए।

मैंने COALESCE . के बीच कुछ अन्य अंतरों के बारे में बात की और ISNULL एक टिप में, जिसका शीर्षक है "SQL सर्वर में COALESCE और ISNULL के बीच निर्णय लेना।" जब मैंने यह लिखा था, तो मैं COALESCE . का उपयोग करने के पक्ष में था उस मामले को छोड़कर जहां पहला तर्क एक सबक्वेरी था (फिर से, इस बग . के कारण "फीचर गैप")। अब मुझे इतना यकीन नहीं है कि मैं इसके बारे में उतना ही दृढ़ता से महसूस करता हूं।

सिंपल CASE एक्सप्रेशन लिंक किए गए सर्वर पर नेस्ट हो सकते हैं

CASE . की कुछ सीमाओं में से एक अभिव्यक्ति यह है कि 10 घोंसले के स्तर तक सीमित है। इस उदाहरण में dba.stackexchange.com पर, पॉल व्हाइट प्रदर्शित करता है (प्लान एक्सप्लोरर का उपयोग करके) कि इस तरह की एक सरल अभिव्यक्ति:

SELECT CASE column_name
  WHEN '1' THEN 'a' 
  WHEN '2' THEN 'b'
  WHEN '3' THEN 'c'
  ...
END
FROM ...

पार्सर द्वारा खोजे गए फ़ॉर्म में विस्तृत हो जाता है:

SELECT CASE 
  WHEN column_name = '1' THEN 'a' 
  WHEN column_name = '2' THEN 'b'
  WHEN column_name = '3' THEN 'c'
  ...
END
FROM ...

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

SELECT 
  CASE WHEN column_name = '1' THEN 'a' ELSE
    CASE WHEN column_name = '2' THEN 'b' ELSE
      CASE WHEN column_name = '3' THEN 'c' ELSE 
      ... 
      ELSE NULL
      END
    END
  END
FROM ...

इस स्थिति में, भले ही मूल क्वेरी में केवल एक ही CASE था 10+ संभावित परिणामों के साथ अभिव्यक्ति, जब लिंक किए गए सर्वर पर भेजा गया, तो इसमें 10+ नेस्टेड . था CASE भाव। जैसे, जैसा कि आप उम्मीद कर सकते हैं, इसने एक त्रुटि लौटा दी:

संदेश 8180, स्तर 16, राज्य 1
विवरण (विवरण) तैयार नहीं किए जा सके।
संदेश 125, स्तर 15, राज्य 4
मामले के भाव केवल स्तर 10 में नेस्ट किए जा सकते हैं।

कुछ मामलों में, आप इसे पॉल के सुझाव के अनुसार फिर से लिख सकते हैं, इस तरह की अभिव्यक्ति के साथ (column_name मानते हुए) एक वर्चर कॉलम है):

SELECT CASE CONVERT(VARCHAR(MAX), SUBSTRING(column_name, 1, 255))
  WHEN 'a' THEN '1'
  WHEN 'b' THEN '2'
  WHEN 'c' THEN '3'
  ...
END
FROM ...

कुछ मामलों में, केवल SUBSTRING उस स्थान को बदलने की आवश्यकता हो सकती है जहां अभिव्यक्ति का मूल्यांकन किया जाता है; अन्य में, केवल CONVERT . मैंने संपूर्ण परीक्षण नहीं किया, लेकिन यह लिंक किए गए सर्वर प्रदाता, कोलेशन कम्पैटिबल और यूज़ रिमोट कॉलेशन जैसे विकल्पों और पाइप के दोनों छोर पर SQL सर्वर के संस्करण के साथ करना पड़ सकता है।

लंबी कहानी छोटी, यह याद रखना महत्वपूर्ण है कि आपका CASE अभिव्यक्ति को बिना किसी चेतावनी के आपके लिए फिर से लिखा जा सकता है, और यह कि आपके द्वारा उपयोग किए जाने वाले किसी भी समाधान को बाद में अनुकूलक द्वारा खारिज किया जा सकता है, भले ही वह अभी आपके लिए काम करता हो।

केस एक्सप्रेशन अंतिम विचार और अतिरिक्त संसाधन

मुझे आशा है कि मैंने CASE के कुछ कम-ज्ञात पहलुओं पर विचार करने के लिए कुछ भोजन दिया है अभिव्यक्ति, और परिस्थितियों में कुछ अंतर्दृष्टि जहां CASE - और कुछ फ़ंक्शन जो समान अंतर्निहित तर्क का उपयोग करते हैं - अप्रत्याशित परिणाम लौटाते हैं। कुछ अन्य दिलचस्प परिदृश्य जहां इस प्रकार की समस्या सामने आई है:

  • स्टैक ओवरफ्लो :यह केस एक्सप्रेशन ELSE क्लॉज तक कैसे पहुंचता है?
  • स्टैक ओवरफ़्लो:CRYPT_GEN_RANDOM () अजीब प्रभाव
  • स्टैक ओवरफ़्लो:चुनें () इरादे के अनुसार काम नहीं कर रहा है
  • स्टैक ओवरफ़्लो:CHECKSUM(NewId()) प्रति पंक्ति कई बार निष्पादित करता है
  • कनेक्ट #350485 :NEWID() और टेबल एक्सप्रेशन के साथ बग

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. शुरुआती के लिए SQL EXISTS ऑपरेटर

  2. SQL में एक रो को कैसे डिलीट करें

  3. RowGen v3 डेटाबेस टेस्ट डेटा जनरेशन को स्वचालित करता है

  4. Exachk उपयोगिता का उपयोग करके Exadata पर स्वास्थ्य जांच

  5. टेबल एक्सप्रेशन के मूल तत्व, भाग 7 - सीटीई, अनुकूलन विचार