लिंक किए गए सर्वरों की CASE अभिव्यक्ति में 10 शाखाओं की सीमा क्यों है?


19

यह CASEअभिव्यक्ति क्यों होती है :

SELECT CASE column 
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        ... c -> i
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END [col] 
FROM LinkedServer.database.dbo.table

इस परिणाम का उत्पादन?

त्रुटि संदेश: Msg 8180, स्तर 16, राज्य 1, पंक्ति 1
कथन (s) तैयार नहीं किया जा सका।
Msg 125, Level 15, State 4, Line 1
केस एक्सप्रेशन केवल लेवल 10 के लिए नेस्टेड हो सकते हैं।

स्पष्ट रूप से यहां एक नेस्टेड CASEअभिव्यक्ति नहीं है, हालांकि 10 से अधिक "शाखाएं हैं।"

एक और विषमता। यह इनलाइन तालिका-मूल्यवान फ़ंक्शन समान त्रुटि उत्पन्न करता है:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
     @var varchar(20)
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table
)

लेकिन इसी तरह का मल्टी-स्टेटमेंट TVF ठीक काम करता है:

ALTER FUNCTION [dbo].[fn_MyFunction]
(   
    @var varchar(20)
)
RETURNS @result TABLE 
(
    value varchar(max)
)
AS
BEGIN
    INSERT INTO @result
    SELECT CASE column 
            WHEN 'a' THEN '1' 
            WHEN 'b' THEN '2' 
            ... c -> i
            WHEN 'j' THEN '10' 
            WHEN 'k' THEN '11'  
        END [col] 
    FROM LinkedServer.database.dbo.table

RETURN;
END

जवाबों:


24

स्पष्ट रूप से CASEयहाँ एक नेस्टेड अभिव्यक्ति नहीं है।

क्वेरी पाठ में नहीं, नहीं। लेकिन पार्सर हमेशा CASEनेस्टेड रूप में अभिव्यक्ति का विस्तार करता है :

SELECT CASE SUBSTRING(p.Name, 1, 1)
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM AdventureWorks2012.Production.Product AS p

स्थानीय क्वेरी योजना

यह क्वेरी स्थानीय है (कोई लिंक नहीं किया गया सर्वर) और कंप्यूट स्केलर निम्नलिखित अभिव्यक्ति को परिभाषित करता है:

नेस्टेड CASE अभिव्यक्ति

स्थानीय रूप से निष्पादित होने पर यह ठीक है, क्योंकि पार्सरCASE 10 स्तरों से अधिक के नेस्टेड स्टेटमेंट को नहीं देखता है (हालांकि यह स्थानीय क्वेरी संकलन के बाद के चरणों में एक पास करता है)।

हालाँकि, लिंक किए गए सर्वर के साथ, उत्पन्न पाठ को संकलन के लिए दूरस्थ सर्वर पर भेजा जा सकता है। यदि ऐसा है, तो दूरस्थ पार्सर एक नेस्टेड देखता हैCASE स्टेटमेंट को 10 से अधिक स्तरों पर और आपको त्रुटि 8180 मिलती है।

एक और विषमता। यह इनलाइन टेबल-वैल्यू फ़ंक्शन समान त्रुटि पैदा करता है

इन-लाइन फ़ंक्शन को मूल क्वेरी पाठ में जगह में विस्तारित किया जाता है, इसलिए यह लिंक किए गए सर्वर के साथ एक ही त्रुटि परिणाम को आश्चर्यचकित नहीं करता है।

लेकिन एक समान मल्टी-स्टेटमेंट TVF ठीक काम करता है

समान, लेकिन समान नहीं। MsTVF में एक अंतर्निहित रूपांतरण शामिल है varchar(max), जो CASEदूरस्थ सर्वर पर भेजे जा रहे अभिव्यक्ति को रोकने के लिए होता है । क्योंकि CASEस्थानीय स्तर पर मूल्यांकन किया जाता है, एक पार्सर कभी भी ओवर-नेस्टेड नहीं देखता है CASEऔर कोई त्रुटि नहीं है। यदि आप तालिका की परिभाषा को बदलते हैंvarchar(max)CASE परिणाम की निहित प्रकार - varchar(2)- अभिव्यक्ति को msTVF के साथ प्रेषित किया जाता है और आपको एक त्रुटि मिलेगी।

अंततः, त्रुटि तब होती है जब CASEदूरस्थ सर्वर द्वारा एक ओवर-नेस्टेड का मूल्यांकन किया जाता है। यदि CASEदूरस्थ क्वेरी पुनरावृत्त में इसका मूल्यांकन नहीं किया जाता है, तो कोई त्रुटि नहीं होती है। उदाहरण के लिए, निम्नलिखित में CONVERTवह शामिल है, जिसे प्रेषित नहीं किया गया है, इसलिए लिंक सर्वर का उपयोग किए जाने के बावजूद कोई त्रुटि नहीं होती है:

SELECT CASE CONVERT(varchar(max), SUBSTRING(p.Name, 1, 1))
        WHEN 'a' THEN '1' 
        WHEN 'b' THEN '2' 
        WHEN 'c' THEN '3' 
        WHEN 'd' THEN '4' 
        WHEN 'e' THEN '5' 
        WHEN 'f' THEN '6' 
        WHEN 'g' THEN '7' 
        WHEN 'h' THEN '8' 
        WHEN 'i' THEN '9' 
        WHEN 'j' THEN '10' 
        WHEN 'k' THEN '11'  
    END
FROM SQL2K8R2.AdventureWorks.Production.Product AS p

CASE रिमूव नहीं किया गया


6

मेरा कूबड़ यह है कि क्वेरी को फिर से कहीं अलग तरीके से लिखा जा रहा है CASE, जैसे कि थोड़ा अलग संरचना है

CASE WHEN column = 'a' THEN '1' ELSE CASE WHEN column = 'b' THEN '2' ELSE ...

मेरा मानना ​​है कि आप जो भी लिंक किए गए सर्वर प्रदाता का उपयोग कर रहे हैं उसमें एक बग है (वास्तव में शायद उनमें से सभी - मैंने देखा है कि यह कई के खिलाफ रिपोर्ट किया गया है)। मेरा यह भी मानना ​​है कि आपको अपनी सांस को एक फिक्स के लिए इंतजार नहीं करना चाहिए, या तो कार्यक्षमता या भ्रमित करने वाले त्रुटि संदेश में व्यवहार की व्याख्या करते हुए - यह लंबे समय से रिपोर्ट किया गया है, इसमें लिंक किए गए सर्वर शामिल हैं (जिसमें SQL के बाद से बहुत प्यार नहीं है सर्वर 2000), और इस भ्रामक त्रुटि संदेश की तुलना में बहुत कम लोगों को प्रभावित करता है करता है, जो एक ही दीर्घायु के बाद अभी तक तय नहीं किया गया है।

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

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

एक और (आपकी क्वेरी वास्तव में यह सरलीकृत है, और आप अक्षर az के संख्यात्मक गुणांक चाहते हैं):

SELECT [col] = RTRIM(ASCII([column])-96)
FROM LinkedServer.database.dbo.table;

यदि आपको इसके लिए काम करने की आवश्यकता है, तो मेरा सुझाव है कि आप सीधे समर्थन से संपर्क करें और एक मामला खोलें, हालांकि मैं परिणामों के लिए वाउचर नहीं कर सकता - वे आपको केवल उन वर्कआर्डरों के साथ प्रदान कर सकते हैं जो आपके पास इस पृष्ठ पर पहले से ही हैं।


5

आप इसके द्वारा प्राप्त कर सकते हैं

SELECT COALESCE(
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'a' THEN '1' 
    WHEN 'b' THEN '2' 
    WHEN 'c' THEN '3' 
    WHEN 'd' THEN '4' 
    WHEN 'e' THEN '5' 
    WHEN 'f' THEN '6' 
    WHEN 'g' THEN '7' 
    WHEN 'h' THEN '8' 
    WHEN 'i' THEN '9' 
    ELSE NULL
END,
CASE SUBSTRING(p.Name, 1, 1)
    WHEN 'j' THEN '10' 
    WHEN 'k' THEN '11'  
END)
FROM SQL2K8R2.AdventureWorks.Production.Product AS p

2

इस समस्या के लिए एक अन्य समाधान सेट आधारित तर्क का उपयोग करना है, CASEएक रेफ़रेंस (या बाहरी लागू) के साथ एक्सप्रेशन को रेफ़रेंस टेबल ( refनीचे दिए गए कोड में) की जगह, जो या तो स्थायी, अस्थायी या एक व्युत्पन्न टेबल / CTE हो सकता है। यदि यह कई प्रश्नों और प्रक्रियाओं में आवश्यक है, तो मैं इसे स्थायी तालिका के रूप में पसंद करूंगा:

SELECT ref.result_column AS [col] 
FROM LinkedServer.database.dbo.table AS t
  LEFT JOIN
    ( VALUES ('a',  '1'),
             ('b',  '2'), 
             ('c',  '3'),
             ---
             ('j', '10'),
             ('k', '11')
    ) AS ref (check_col, result_column) 
    ON ref.check_col = t.column ;

-4

इसका एक तरीका यह है कि परीक्षण को whenक्लॉज में शामिल किया जाए

case
  when SUBSTRING(p.Name, 1, 1) = 'a' THEN '1'
...

दरअसल नहीं। दोनों SELECT CASE v.V WHEN 'a' THEN 1 WHEN 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);और एक ही निष्पादन योजना के SELECT CASE WHEN v.V = 'a' THEN 1 WHEN v.V = 'b' THEN 2 END FROM (VALUES ('a'), ('b')) AS v (V);लिए अनुवाद करते हैं (खुद के लिए यह सत्यापित करने के लिए स्वतंत्र महसूस करें), जहां CASE अभिव्यक्ति को फिर से परिभाषित किया गया है CASE WHEN [Union1002]='a' THEN (1) ELSE CASE WHEN [Union1002]='b' THEN (2) ELSE NULL END END- जैसे घोंसले के शिकार, जैसा कि आप देख सकते हैं।
एंड्री एम
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.