我在TVF内宣布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选项的情况下运行正常 但是该选项存在语法错误。 解决方案是什么?
问候
答案 0 :(得分:40)
[{1]}子句只能在语句级别使用
因此,您无法在视图定义或内联TVF内的查询表达式中使用它。在您的情况下使用它的唯一方法是创建没有
OPTION
子句的TVF并在使用的查询中指定它TVF。我们有一个错误跟踪允许在任何查询表达式中使用OPTION
子句的请求(例如,OPTION
或CTE或视图)。
并进一步
您无法在udf中更改该选项的默认值。您 必须在引用udf的语句中进行。
因此,在您的示例中,当您调用您的函数时,您必须指定if exists()
:
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
)
请注意,你不能通过让第二个TVF执行上述操作来解决这个问题 - 如果你尝试,你会得到同样的错误。 “[{] SELECT * FROM [liste_jour] ( @from , @to ) OPTION ( MAXRECURSION 365 )
子句只能在语句级别使用”,这是最终的(现在)。
答案 1 :(得分: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
这可能存在效率问题,但我可以负担得起。
答案 2 :(得分: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
)
答案 3 :(得分:2)
旧问题但是......我只是想澄清为什么OPTION(MAXRECURSION x)不允许在内联表值函数中。这是因为在查询中使用iTVF的内联。而且,众所周知,除了查询的最后,你不能把这个选项放在其他任何地方。这是 的原因,永远不可能把它放在iTVF中(除非解析器和/或algebrizer在幕后做了一些魔术,我认为不会很快)。 mTVF(多语句表值函数)是一个不同的故事,因为它们没有内联(并且速度很慢,以至于它们永远不应该在查询中使用;但是可以在赋值给变量时使用它们,但是然后再次 - 小心循环!)。
答案 4 :(得分:0)
对CTE和笛卡尔产品(交叉连接)的一点创造性使用将使您在MAXRECURSION
限制为100左右。最后一个限制为4条记录的CTE将为您提供40,000条记录,这将是有利于超过100年的数据。如果您希望@debut和@fin之间存在更多差异,则可以调整cte3
。另外,请停止SHOUTING您的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')
答案 5 :(得分:0)
为您创建简单的示例:)
/ *块创建功能以在sql * /中进行测试
/ * FUNCTION [fn_CTE_withLevel](@max_level int)
退货表
如
返回
(* /
/ *******************声明表只替换真实表***** /
declare @tbl table(pid varchar(15),id varchar(15))
/* use function argument */
declare @max_level int = 3
Insert Into @tbl(pid , id)
values
/*lev1*/ ('0','1') ,
/*lev2*/ ('1','101') ,
/*lev2*/ ('1','102') ,
/*lev1*/ ('0','2') ,
/*lev2*/ ('2','201') ,
/*lev3*/ ('201','20101') ,
/*lev3*/ ('201','20102') ,
/*lev2*/ ('2','202') ,
/*lev1*/ ('0','3') ,
/*lev2*/ ('3','301') ,
/*lev2*/ ('3','302') ,
/*lev1*/ ('0','4') ,
/*lev2*/ ('4','401'),
/*lev2*/ ('4','402');
/ *******************声明表只替换真实表***** /
With cte_result(pid , id , lev)
As(
Select pid , id , 1 as lev From @tbl t
Where pid = '0' /* change to another values from list to test sub items */
Union All
Select t.pid , t.id , cte.lev + 1 as lev
From cte_result cte
inner Join @tbl t
On cte.id = t.pid
Where cte.lev < @max_level -- :) this is my idea
)
Select * From cte_result
--OPTION (MAXRECURSION 100)
-取消对创建功能的注释 / ) /