如何创建参数化递归 CTE 以展平标量函数中的层次结构?

How does one Create a Parameterized Recursive CTE to flatten a heirarchy within a Scalar Function?

我正在尝试创建一个标量函数来确定所提供 ID 的用户或其任何下属是否在所提供的订单 ID 集合下有订单。

请注意,我正在使用我自己的 Table 类型 IntegerIdTableType 来接收 OrderIds 的集合。

CREATE FUNCTION DoOrdersExistUnderUserOrUsersSubordinates
(
    @orderIds dbo.IntegerIdTableType READONLY,
    @userId INT
)
RETURNS BIT
AS
BEGIN
    RETURN 
    (
        WITH GetUserIds(ordinateUserId)
        AS
        (
            SELECT ordinateUserId UserId
            UNION ALL
            SELECT GetUserIds(Subordinate.Id)
            FROM UsersAccounts.Users Subordinates
            WHERE Subordinates.SupervisorId = @ordinateUserId
        )
        SELECT CASE WHEN EXISTS
        (
            SELECT 1
            FROM Orders
            WHERE Orders.Id IN
            (
                SELECT Id
                FROM @orderIds
            )
            AND Orders.UserId IN
            (
                SELECT UserId
                FROM GetUserIds(@userId)
            )
        )
        THEN CAST(1 AS BIT)
        ELSE CAST(0 AS BIT)
        END
    )
END

这是我的 OrdersUsers tables.

的一些示例数据

用户

订单

预期结果

使用以下值调用 DoOrdersExistUnderUserOrUsersSubordinates 时,我希望得到以下结果。

这个函数有 2 个问题:

  1. 语法错误:

    Incorrect syntax near the keyword 'WITH'.

    Incorrect syntax near ')'.

  2. 'GetUserIds' is not a recognized built-in function name

    即使没有包装在函数中,上述情况似乎也会发生。

我不知道将参数传递给递归 CTE 的正确方法是什么,但我看到了一些示例,其中 CTE 的声明在括号中有一个名称,我认为它是一个参数

我试过在 WITH 之前放一个分号,尽管它是函数中唯一的语句,但我只是得到 Incorrect syntax near ';'. 而不是 关键字 'WITH' 附近的语法不正确

我也试过去掉 BEGINEND,这让我 'RETURN'. 附近的语法不正确,另外关键字 'with' 附近的语法不正确。如果这个语句是一个普通的table表达式,一个xmlnamespaces子句或者一个更改跟踪上下文子句,前面的语句必须以分号结束。如果我不包括多余的分号。

我该如何解决所有这些问题?

当然,递归 CTE 必须能够接受参数,否则它们会递归什么?

更新:

在与 Zohar_Peled 链接的文档的 Example F 进行了较量之后,我最终发现参数并没有像这样传递到 CTE,而是加入了然后通过声明的括号在其中坚持下去。然后在相应的 SELECTs 中定义的任何内容都通过参数输出到称为 CTE 的任何内容(在这种情况下,外部 SELECT Id FROM UserNodes 语句或 CTE 本身(用于递归))。

我将函数内的 SQL 语句更改为以下内容,它在函数外按预期工作。

WITH UserNodes([Root User ID], Id, SupervisorId)
AS
(
    SELECT Users.Id, Users.Id, Users.SupervisorId
    FROM UsersAccounts.Users
    WHERE Users.SupervisorId IS NULL
    UNION ALL
    SELECT [Root User ID],
        Users.Id,
        Users.SupervisorId
    FROM UsersAccounts.Users
    JOIN UserNodes [Subordinate Descendant Users] ON [Subordinate Descendant Users].Id = Users.SupervisorId
)
SELECT CASE WHEN EXISTS
(
    SELECT 1
    FROM Orders
    WHERE Orders.Id IN
    (
        SELECT Id
        FROM @orderIds
    )
    AND Orders.UserId IN
    (
        SELECT Id
        FROM UserNodes
        WHERE [Root User ID] = @userId
    )
)
THEN CAST(1 AS BIT)
ELSE CAST(0 AS BIT)
END

这单独工作很好(提供了必需的变量来替换缺少的函数参数)但是一旦我把它放回 CREATE FUNCTION 块,我就遇到了和以前一样的语法错误(不包括 2.)。

如前所述,我无法对此进行测试,但这是我建议您更改的内容:

    CREATE FUNCTION DoOrdersExistUnderUserOrUsersSubordinates
(
    @orderIds dbo.IntegerIdTableType READONLY,
    @userId INT
)
RETURNS BIT
AS
BEGIN

    declare @bln bit

    ;WITH UserNodes([Root User ID], Id, SupervisorId)
    AS
    (
        SELECT Users.Id, Users.Id, Users.SupervisorId
        FROM UsersAccounts.Users
        WHERE Users.SupervisorId IS NULL
        UNION ALL
        SELECT [Root User ID],
            Users.Id,
            Users.SupervisorId
        FROM UsersAccounts.Users
        JOIN UserNodes [Subordinate Descendant Users] ON [Subordinate Descendant Users].Id = Users.SupervisorId
    )
    SELECT @bln = CASE WHEN EXISTS
    (
        SELECT 1
        FROM Orders
        WHERE Orders.Id IN
        (
            SELECT Id
            FROM @orderIds
        )
        AND Orders.UserId IN
        (
            SELECT Id
            FROM UserNodes
            WHERE [Root User ID] = @userId
        )
    )
    THEN CAST(1 AS BIT)
    ELSE CAST(0 AS BIT)
    END

    RETURN @bln
END

让我知道它是否有效...