2011-09-15 31 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子句可以在語句級別僅用於

所以你不能在查詢表達式中使用視圖定義裏面或內聯TVF等。在你的情況下使用它的唯一方法是創建沒有OPTION子句的TVF,並在使用TVF的查詢中指定它。我們有一個跟蹤允許在任何查詢表達式中使用OPTION子句的請求的錯誤(例如,if exists()或CTE或視圖)。

,並進一步

你不能改變一個UDF中該選項的默認值。你在 將不得不在引用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) 

請注意,你不能圓這個工作通過讓第二個TVF執行上述操作 - 如果您嘗試,您會得到相同的錯誤。 「[子句只能在語句級別使用」,這是最後的(現在)。

22

舊線,我知道,但我需要同樣的事情,只是通過使用多語句UDF處理它:

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

這個答案可能會好一點,因爲在函數中沒有必要記住關於CTE(沒有遞歸限制)。 –

+0

當然,它解決了問題,但它有「可能的缺點」,因爲這個表值函數是「非內聯」的。在性能方面它可能是非常殘酷的。 – pkuderov

+0

正如我所說,「這可能有效率問題,但我可以負擔得起這個。」 – Crisfole

2

解決這個問題的另一種方法是將問題分解爲一對CTE,它們都沒有達到100的遞歸限制。第一個CTE創建一個列表,該範圍中每個月的開始日期。第二個CTE隨後填寫每個月的所有日子。只要輸入範圍少於100個月,它應該可以正常工作。如果需要超過100個月的輸入範圍,則在CTE月份之前添加第三個CTE可以延長相同的想法。

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

老問題,但...我只是想澄清爲什麼OPTION(MAXRECURSION x)不允許在一個內聯表值函數。這是因爲iTVF的在查詢中使用它們時會獲得內聯。而且,衆所周知,除了查詢的最後,您無法將此選項放在其他任何位置。這是THE因爲它永遠不可能放在iTVF裏(除非解析器和/或代理器在幕後做了一些魔術,我認爲它不會很快)。 mTVF(多語句表值函數)是一個不同的故事,因爲它們沒有被內聯(而且太慢以至於它們不應該被用於查詢;儘管如此,它們也可以用於賦值給變量,但是然後再次---謹防循環!)。

0

CTE和笛卡爾產品(交叉連接)的一點創意使用將使您獲得約爲100的MAXRECURSION的限制。3個CTE限制4條記錄,最後一條記錄可獲得40,000條記錄,這對於更多超過100年的數據。如果您期望@debut和@fin之間有更多差異,您可以調整cte3。另外,請停止發送你的SQL。

-- 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') 
相關問題