2011-09-15 32 views
31

मैं एक समस्या एक TVF अंदर एक CTE के लिए maxrecursion विकल्प घोषित करने के लिए का सामना करना पड़ रहा हूँकैसे सेटअप करने के लिए एक टेबल-वैल्यूड-समारोह के अंदर एक CTE के लिए maxrecursion विकल्प

यहाँ

है CTE (एक सरल कैलेंडर):

DECLARE @DEBUT DATE = '1/1/11', @FIN DATE = '1/10/11'; 

WITH CTE as(  
SELECT @debut as jour  
UNION ALL  
SELECT DATEADD(day, 1, jour)  
FROM CTE  
WHERE DATEADD(day, 1, jour) <= @fin) 
SELECT jour FROM CTE option (maxrecursion 365) 

और TVF:

CREATE FUNCTION [liste_jour] 
(@debut date,@fin date) 
RETURNS TABLE 
AS  
RETURN  
( 
    WITH CTE as(  
    SELECT @debut as jour  
    UNION ALL  
    SELECT DATEADD(day, 1, jour)  
    FROM CTE  
    WHERE DATEADD(day, 1, jour) <= @fin) 
    SELECT jour FROM CTE 
    --option (maxrecursion 365) 
) 
TVF ऊपर

maxrecursion विकल्प बिना ठीक चल रहा है लेकिन वहाँ विकल्प के साथ एक सिंटैक्स त्रुटि है। समाधान क्या है?

संबंध

उत्तर

37

this MSDN forums thread से मैं समझ जाते हैं कि

[यह] OPTION खंड बयान के स्तर पर ही इस्तेमाल किया जा सकता

तो तुम दृश्य परिभाषाओं के अंदर एक प्रश्न अभिव्यक्ति के भीतर इसका इस्तेमाल नहीं कर सकते हैं या इनलाइन टीवीएफ इत्यादि। अपने मामले में इसका उपयोग करने का एकमात्र तरीका OPTION खंड के बिना टीवीएफ बनाना है और टीवीएफ का उपयोग करने वाली क्वेरी में निर्दिष्ट करना है। हमारे पास एक बग है जो OPTION किसी भी क्वेरी अभिव्यक्ति के अंदर खंड (उदाहरण के लिए, if exists() या सीटीई या दृश्य) के उपयोग की अनुमति देने के लिए अनुरोध ट्रैक करता है।

और आगे

आप एक यूडीएफ के अंदर उस विकल्प का डिफ़ॉल्ट मान नहीं बदल सकते। आप को udf को संदर्भित कथन में करना होगा।

तो अपने उदाहरण में, आप OPTION जब आप कॉल अपने कार्य उल्लेख करना होगा:

CREATE FUNCTION [liste_jour] 
(@debut date,@fin date) 
RETURNS TABLE 
AS  
RETURN  
( 
    WITH CTE as(  
    SELECT @debut as jour  
    UNION ALL  
    SELECT DATEADD(day, 1, jour)  
    FROM CTE  
    WHERE DATEADD(day, 1, jour) <= @fin) 
    SELECT jour FROM CTE -- no OPTION here 
) 

(बाद में)

SELECT * FROM [liste_jour] (@from , @to) OPTION (MAXRECURSION 365) 

ध्यान दें कि आप इस दौर काम नहीं कर सकता एक दूसरा टीवीएफ होने पर जो उपर्युक्त रेखा करता है - यदि आप कोशिश करते हैं तो आपको वही त्रुटि मिलती है। "[0] OPTION खंड केवल कथन स्तर पर उपयोग किया जा सकता है", और यह अंतिम (अभी के लिए) है।

22

पुराना धागा, मुझे पता है, लेकिन मैं एक ही बात की जरूरत है और सिर्फ एक बहु बयान यूडीएफ का उपयोग करके इसे से निपटा:

CREATE FUNCTION DatesInRange 
(
    @DateFrom datetime, 
    @DateTo datetime 
) 
RETURNS 
@ReturnVal TABLE 
(
    date datetime 
) 
AS 
BEGIN 

    with DateTable as (
     select dateFrom = @DateFrom 

     union all 

     select DateAdd(day, 1, df.dateFrom) 
     from DateTable df 
     where df.dateFrom < @DateTo 
    ) 
    insert into @ReturnVal(date) 

    select dateFrom 

    from DateTable option (maxrecursion 32767) 

    RETURN 
END 
GO 

संभवतः इस के साथ दक्षता मुद्दे हैं, लेकिन मैं इसे में खर्च कर सकते हैं मेरा मामला।

+3

यह उत्तर थोड़ा बेहतर हो सकता है, क्योंकि किसी फ़ंक्शन में सीटीई (रिकर्सन सीमा के बिना) को याद रखने की आवश्यकता नहीं है। –

+0

बेशक यह समस्या हल करता है, लेकिन इसमें "संभवतः एक कमी" है, क्योंकि यह तालिका-मूल्यवान फ़ंक्शन 'गैर-रेखांकित' है। प्रदर्शन के मामले में यह क्रूसियल हो सकता है। – pkuderov

+0

जैसा कि मैंने कहा था, "इसके साथ शायद दक्षता समस्याएं हैं, लेकिन मैं इसे अपने मामले में बर्दाश्त कर सकता हूं।" – Crisfole

2

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

CREATE FUNCTION [liste_jour]  
(@debut datetime, @fin datetime)  
RETURNS TABLE 
AS  
RETURN   
( 
    WITH CTE_MOIS AS 
    (   
     SELECT JOUR_DEBUT = @debut 
     UNION ALL 
     SELECT DATEADD(MONTH, 1, CTE_MOIS.JOUR_DEBUT) 
      FROM CTE_MOIS   
     WHERE DATEADD(MONTH, 1, CTE_MOIS.JOUR_DEBUT) <= @fin 
    ), 

    CTE_JOUR AS 
    (   
     SELECT JOUR = CTE_MOIS.JOUR_DEBUT 
      FROM CTE_MOIS 
     UNION ALL   
     SELECT DATEADD(DAY, 1, CTE_JOUR.JOUR) 
      FROM CTE_JOUR 
     WHERE MONTH(CTE_JOUR.JOUR) = MONTH(DATEADD(DAY, 1, CTE_JOUR.JOUR)) AND 
      DATEADD(DAY, 1, CTE_JOUR.JOUR) <= @FIN 
    ) 

    SELECT JOUR 
     FROM CTE_JOUR 
) 
2

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

0

सीटीई और कार्तीय उत्पादों (पार मिलती है) पिछले एक जाल आप 40,000 रिकॉर्ड पर 4 रिकॉर्ड की एक सीमा के साथ 100 के 3 सीटीई की MAXRECURSION सीमा, जो और अधिक के लिए अच्छा होगा आप के आसपास मिल जाएगा की एक छोटी सी रचनात्मक उपयोग डेटा के 100 साल से अधिक मूल्य। यदि आप @debut और @fin के बीच अधिक अंतर की अपेक्षा करते हैं, तो आप cte3 समायोजित कर सकते हैं। इसके अलावा, कृपया अपने एसक्यूएल को बंद करना बंद करें।

-- please don't SHOUTCASE your SQL anymore... this ain't COBOL 
alter function liste_jour(@debut date, @fin date) returns table as 
return ( 
    with cte as (
     select 0 as seq1 
     union all 
     select seq1 + 1 
     from cte 
     where seq1 + 1 < 100 
    ), 
    cte2 as (
     select 0 as seq2 
     union all 
     select seq2 + 1 
     from cte2 
     where seq2 + 1 < 100 
    ), 
    cte3 as (
     select 0 as seq3 
     union all 
     select seq3 + 1 
     from cte3 
     where seq3 + 1 <= 3 -- increase if 100 years isn't good enough 
    ) 
    select 
     dateadd(day, (seq1 + (100 * seq2) + (10000 * seq3)), @debut) as jour 
    from cte, cte2, cte3 
    where (seq1 + (100 * seq2) + (10000 * seq3)) <= datediff(day, @debut, @fin) 
) 
go 
-- test it! 
select * from liste_jour('1/1/2000', '2/1/2000')