我的情况是,我需要能够查看某个人是否在用户/经理层次结构中。
我需要能够针对一组规则为一组用户执行此操作(不要担心这一点,只是为了给它一些上下文)
理想情况下,我想在where子句的相关存在子查询中使用递归CTE。
但这会带来很多问题。
我认为我要做的是:
WITH UserHierarchy(UserId, ManagerId)
AS
(
--Anchor Definition
SELECT [UserId], [ManagerId] FROM [Users] WHERE [ManagerId] = [Rules].[RuleAddedByUserId] -- this needs to bind to an outer query....
UNION ALL
--Recursive Member definiation
SELECT [Users].[UserId], [Users].[ManagerId] FROM [Users]
INNER JOIN [UserHierarchy] ON [Users].[ManagerId] = [UserHierarchy].[UserId]
WHERE [Users].[UserId] <> [Users].[ManagerId] --don't recurse if the anchor definition matches itself (to avoid an infinate loop).
)
无论如何在兼容模式80中使锚定义动态化吗?还是另一种方法?
答案 0 :(得分:2)
这样做的一种方法是创建一个递归CTE,对于每个用户,树中该用户的每个祖先都有一行。然后,您可以使用CTE过滤祖先。例如,使用此树:
Bob
|-Alice
|-Jim
CTE会返回类似的内容:
User Ancestor Level
---- -------- -----
Bob NULL 1
Alice Bob 1
Jim Alice 1
Jim Bob 2
Level
列最终不是很重要,但我在编写查询时发现它有所帮助。
这是一个示例脚本,用于标识层次结构中Alice下的所有用户:
CREATE TABLE Users(
UserId int NOT NULL PRIMARY KEY,
Name nvarchar(25),
ManagerId int
);
GO
INSERT INTO Users (UserId, Name, ManagerId)
SELECT 1, 'Bob', NULL UNION ALL
SELECT 2, 'Steve', 1 UNION ALL
SELECT 3, 'Chris', 2 UNION ALL
SELECT 4, 'Alice', 1 UNION ALL
SELECT 5, 'Roger', 4 UNION ALL
SELECT 6, 'Tony', 5;
GO
WITH all_ancestors AS (
SELECT
u.UserId,
u.Name,
u.ManagerId AS AncestorId,
1 AS level
FROM
Users AS u
UNION ALL
SELECT
alla.UserId,
alla.Name,
u.ManagerId AS AncestorId,
alla.level + 1
FROM
all_ancestors AS alla
INNER JOIN
Users AS u
ON
alla.AncestorId = u.UserId
)
SELECT
u.*
FROM
Users AS u
INNER JOIN
all_ancestors AS a
ON
u.UserId = a.UserId
WHERE
a.AncestorId = 4; -- Alice
GO
DROP TABLE Users;
GO
答案 1 :(得分:1)
Users
表中可以有多少级别的层次结构?我希望它相当低。我想知道它是否足够低,可以尝试多个嵌套EXISTS
测试,如下所示:
… /* your main query here */
WHERE …
AND EXISTS (
SELECT *
FROM [Users] u1
WHERE [UserID] = @UserID
AND (
[ManagerId] = [Rules].[RuleAddedByUserId]
OR EXISTS (
SELECT *
FROM [Users] u2
WHERE [UserID] = u1.[ManagerID]
AND (
[ManagerId] = [Rules].[RuleAddedByUserId]
OR EXISTS (
SELECT *
FROM [Users] u3
WHERE [UserID] = u2.[ManagerID]
AND (
[ManagerId] = [Rules].[RuleAddedByUserId]
OR EXISTS ( … /* and so on, until you've covered
all possible levels */
)
)
)
)
)
)
)
答案 2 :(得分:0)
我最终到了那里!谢谢大家的帮助。
这是我正在研究的SQL的片段。
我只需要改变我的想法,而不是看看在where子句中的管理员下是否存在用户。我需要考虑CTE一个预过滤器,然后构建我需要的所有细节,然后在存在语句之后进行常规过滤(n.b.我没有包括那些为了简洁)。
RulesUserHierarchy(UserId, ManagerId, PushRuleId, OnlyForSubOrdinates) -- Gets only subordinates for rules created by managers. And all users for those created by admin.
AS
(
--Anchor Definition
SELECT
[Users].[UserId]
,[Users].[ManagerId]
,[RulesAnchor].[PushRuleId]
,[RulesAnchor].[OnlyForSubOrdinates]
FROM [Users]
CROSS JOIN [Rules] [RulesAnchor] --assume every user is doing every rule at this point (because the recursive statement has to be the first statement), we'll filter later.
WHERE (([OnlyForSubOrdinates]) = 0 OR ([OnlyForSubOrdinates] = 1 AND [UserId] = [RulesAnchor].[AddedByUserId]))
UNION ALL
--Recursive Member definiation
SELECT
[Users].[UserId]
,[Users].[ManagerId]
,[RulesUserHierarchy].[PushRuleId]
,[RulesUserHierarchy].[OnlyForSubOrdinates]
FROM [Users]
INNER JOIN [RulesUserHierarchy]
ON [Users].[ManagerId] = [RulesUserHierarchy].[UserId] --recursive hook
AND [RulesUserHierarchy].[OnlyForSubOrdinates] = 1 -- no point recursing if it's for everyone, as the anchor will pull back everything for us.
WHERE [Users].[UserId] <> [Users].[ManagerId] --don't recurse if the anchor definition matches itself (to avoid an infinate loop).
)
-- simple statement to test recursion above, will be filtering the inclusions here (e.g. the other mega exists statements)
SELECT [UserId], [ManagerId], [PushRuleId], [OnlyForSubOrdinates] FROM [RulesUserHierarchy]
修改强>
我意识到我不需要递归定义中的交叉连接。虽然它不会影响后者存在的陈述的结果,但它对表现并不好。