如何创建参数化递归 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
这是我的 Orders 和 Users tables.
的一些示例数据
用户
订单
预期结果
使用以下值调用 DoOrdersExistUnderUserOrUsersSubordinates
时,我希望得到以下结果。
这个函数有 2 个问题:
语法错误:
Incorrect syntax near the keyword 'WITH'.
Incorrect syntax near ')'.
'GetUserIds' is not a recognized built-in function name
即使没有包装在函数中,上述情况似乎也会发生。
我不知道将参数传递给递归 CTE 的正确方法是什么,但我看到了一些示例,其中 CTE 的声明在括号中有一个名称,我认为它是一个参数
我试过在 WITH
之前放一个分号,尽管它是函数中唯一的语句,但我只是得到 Incorrect syntax near ';'. 而不是 关键字 'WITH' 附近的语法不正确
我也试过去掉 BEGIN
和 END
,这让我 'RETURN'. 附近的语法不正确,另外关键字 'with' 附近的语法不正确。如果这个语句是一个普通的table表达式,一个xmlnamespaces
子句或者一个更改跟踪上下文子句,前面的语句必须以分号结束。如果我不包括多余的分号。
我该如何解决所有这些问题?
当然,递归 CTE 必须能够接受参数,否则它们会递归什么?
更新:
在与 Zohar_Peled 链接的文档的 Example F 进行了较量之后,我最终发现参数并没有像这样传递到 CTE,而是加入了然后通过声明的括号在其中坚持下去。然后在相应的 SELECT
s 中定义的任何内容都通过参数输出到称为 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
让我知道它是否有效...
我正在尝试创建一个标量函数来确定所提供 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
这是我的 Orders 和 Users tables.
的一些示例数据用户
订单
预期结果
使用以下值调用 DoOrdersExistUnderUserOrUsersSubordinates
时,我希望得到以下结果。
这个函数有 2 个问题:
语法错误:
Incorrect syntax near the keyword 'WITH'.
Incorrect syntax near ')'.
'GetUserIds' is not a recognized built-in function name
即使没有包装在函数中,上述情况似乎也会发生。
我不知道将参数传递给递归 CTE 的正确方法是什么,但我看到了一些示例,其中 CTE 的声明在括号中有一个名称,我认为它是一个参数
我试过在 WITH
之前放一个分号,尽管它是函数中唯一的语句,但我只是得到 Incorrect syntax near ';'. 而不是 关键字 'WITH' 附近的语法不正确
我也试过去掉 BEGIN
和 END
,这让我 'RETURN'. 附近的语法不正确,另外关键字 'with' 附近的语法不正确。如果这个语句是一个普通的table表达式,一个xmlnamespaces
子句或者一个更改跟踪上下文子句,前面的语句必须以分号结束。如果我不包括多余的分号。
我该如何解决所有这些问题?
当然,递归 CTE 必须能够接受参数,否则它们会递归什么?
更新:
在与 Zohar_Peled 链接的文档的 Example F 进行了较量之后,我最终发现参数并没有像这样传递到 CTE,而是加入了然后通过声明的括号在其中坚持下去。然后在相应的 SELECT
s 中定义的任何内容都通过参数输出到称为 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
让我知道它是否有效...