Table 具有递归 CTE 的值函数

Table Valued Function with Recursive CTE

只是为了好玩,我正在尝试编写一个 table 值函数来生成 table 日期。出于测试目的,我硬编码了应该在变量中传递的值。

这本身有效:

WITH cte AS (
    SELECT cast('2021-10-01' AS date) AS date
    UNION ALL
    SELECT dateadd(day,1,date) FROM cte WHERE date<current_timestamp
)
SELECT * FROM cte OPTION(maxrecursion 0);

注意最后的OPTION

作为一个函数,除非我删除末尾的 OPTION 子句,否则它不会工作:

CREATE FUNCTION dates(@start date, @rows INT) RETURNS TABLE AS
RETURN
WITH cte AS (
    SELECT cast('2021-10-01' AS date) AS date
    UNION ALL
    SELECT dateadd(day,1,date) FROM cte WHERE date<current_timestamp
)
SELECT * FROM cte   --  OPTION(maxrecursion 0)
;

测试数据还行,如果我给它年初的日期肯定会失败,因为它涉及100多次递归。

是否有正确的语法,或者这是另一个需要解决方法的 Microsoft Quirk?

认为这可能就是答案。

@GarethD 在回答另一个问题时说:

If you think of a view more as a stored subquery than a stored query … and remember that its definition is expanded out into the main query …

(Incorrect Syntax Near Keyword 'OPTION' in CTE Statement).

如果是这样,视图就不能包含任何不能包含在子查询中的内容。这包括 ORDER BY 子句和 OPTION.

等提示

我还没有确定,但我猜想 Table 值函数也是如此。如果是这样,答案是否定的,没有办法包含 OPTION 子句。

请注意,其他 DBMS 在子查询中可以包含的内容方面更为宽松,因此我不认为它们具有相同的限制。

解决方案是使用多语句Table值函数:

DROP FUNCTION IF EXISTS dates;
GO
CREATE FUNCTION dates(@start date, @end date) RETURNS @dates TABLE(date date) AS
BEGIN
    WITH cte AS (
        SELECT @start AS date
        UNION ALL
        SELECT dateadd(day,1,date) FROM cte WHERE date<@end
    )
    INSERT INTO @dates(date)
    SELECT date FROM cte OPTION(MAXRECURSION 0)
    RETURN;
END;
GO
SELECT * FROM dates('2020-01-01','2021-01-01');

Inline Table Value Functions 是字面上的内联,OPTION 等子句只能出现在 SQL 语句的末尾,不一定在语句的末尾内联函数。

另一方面,多语句 Table 值函数是真正独立的,因此 OPTION 子句在那里是可以的。