我有一个描述层次结构的表:
Name MemberName
A B
A C
B D
D E
F G
MemberName引用同一个表的Name列。从这张表中,我可以轻松查询B和C是A中的成员,D是B的成员,E是D的成员,G是F的成员。
基于这种结构,很难编写一个查询,表明D和E也间接成为A的成员.D和E也间接成为B的成员,等等。所以我需要做的是建立显示所有间接成员的新表。因此,对于上面的表数据,我最终会得到一个包含以下内容的表:
Name MemberName
A B
A C
A D
A E
B D
B E
D E
F G
我首先将所有不是其他记录(顶级)记录成员的记录放入临时表中:
CREATE TABLE #TMP
(
[Name] varchar(20),
[MemberName] varchar(20)
)
DECLARE @iRowsFound INT
INSERT INTO #TMP ([Name],[MemberName])
(SELECT * FROM [HierarchyData] WHERE [Name] NOT IN
(SELECT [MemberName] FROM [HierarchyData]))
SELECT @iRowsFound = @@ROWCOUNT
Name MemberName
A B
A C
F G
然后我的理论是,在一个while循环中,将临时表交叉连接到heirachy表,并将来自交叉连接的适用记录插回到临时表中,并执行while循环,直到没有更多适用的记录在交叉连接中插入:
WHILE (@iRowsFound > 0)
BEGIN
INSERT INTO #TMP ([Name],[MemberName])
(
SELECT
[NewName] = ??,
[NewMember] = ??
FROM
[HierarchyData],[#TMP]
WHERE
???
)
SELECT @iRowsFound = @@ROWCOUNT
END
我只是不确定自己是否在正确的轨道上,因为我对交叉连接选择应该是什么感觉有点难过。有没有人做过这样的事情(在sql server 2000中)?
编辑:我想我可能已经知道了: - 虽然我很确定必须有 更有效的方法......
WHILE (@iRowsFound > 0)
BEGIN
INSERT INTO #TMP ([Name],[MemberName])
(
SELECT
--[#TMP].[Name],
--[#TMP].[MemberName],
[HierarchyData].[Name],
[HierarchyData].[MemberName]
FROM
[#TMP]
JOIN
[HierarchyData] ON [#TMP].[MemberName] = [HierarchyData].[Name]
--WHERE
-- [#TMP].[MemberName] = [HierarchyData].[Name]
AND NOT EXISTS (SELECT * FROM [#TMP] WHERE [#TMP].[Name] = [HierarchyData].[Name] AND [#TMP].[MemberName] = [HierarchyData].[MemberName])
UNION
SELECT
[#TMP].[Name],
--[#TMP].[MemberName],
--[HierarchyData].[Name],
[HierarchyData].[MemberName]
FROM
[#TMP]
JOIN
[HierarchyData] ON [#TMP].[MemberName] = [HierarchyData].[Name]
AND NOT EXISTS (SELECT * FROM [#TMP] WHERE [#TMP].[Name] = [#TMP].[Name] AND [#TMP].[MemberName] = [HierarchyData].[MemberName])
)
SELECT @iRowsFound = @@ROWCOUNT
END
答案 0 :(得分:6)
很遗憾你不在sql server 2005或更高版本上,使用递归CTE很容易,代码就在这里:
WITH Members AS
(
Select Name, MemberName
FROM HierarchyData
UNION ALL
SELECT Name, Child.MemberName as [MemberName]
FROM Members
JOIN HierarchyData Child ON Members.MemberName = Child.Name
)
SELECT * FROM Members
在2000年你可以基本上以相同的方式做到(将最后一个选择的结果加到原始表中,直到你没有循环中最后一个结果的结果),但它要困难得多,因为你必须跟踪您通过计数器进行的迭代。呸。
这有用吗,或者你想要一些sql 2000伪代码吗?
更好的是,只需升级!
答案 1 :(得分:3)
这是一个SQL 2000版本。
一些注意事项:这将适用于任意数量的级别,并且不会出现循环错误(如CTE版本所示)。
declare @lastcount int
declare @lastcycle int
Select HierarchyData.Name, HierarchyData.MemberName, 0 as [Cycle] INTO #list
FROM HierarchyData
SET @lastcount = @@rowcount
SET @lastcycle = 0
while @lastcount > 0
BEGIN
INSERT INTO #list
SELECT Members.Name, Child.MemberName as [MemberName], @lastcycle+1 as [Cycle]
FROM #list Members
JOIN HierarchyData Child ON Members.MemberName = Child.Name
LEFT JOIN #list cycletest ON Members.Name = cycletest.Name AND Child.MemberName = cycletest.Membername
WHERE Members.Cycle = @lastcycle AND NOT (Members.Name = Child.MemberName) AND cycletest.Name is null
SET @lastcount = @@rowcount
SET @lastcycle = @lastcycle + 1
END
SELECT [Name], [MemberName] FROM #list
ORDER BY [Name], [MemberName]
DROP TABLE #list
---- Test data
--create table HierarchyData
--(
-- [Name] varchar(20),
-- [MemberName] varchar(20)
--)
--
--INSERT INTO HierarchyData (Name,MemberName) Values('A','B')
--INSERT INTO HierarchyData (Name,MemberName) Values('A','C')
--INSERT INTO HierarchyData (Name,MemberName) Values('B','D')
--INSERT INTO HierarchyData (Name,MemberName) Values('D','E')
--INSERT INTO HierarchyData (Name,MemberName) Values('F','G')
----CYCLE TEST (the CTE will not work)
--INSERT INTO HierarchyData (Name,MemberName) Values('E','D')
--
---- Test
--select * from HierarchyData
---- CTE Works (note, will fail on cycles.)
--WITH Members AS
--(
-- Select HierarchyData.Name, HierarchyData.MemberName
-- FROM HierarchyData
-- UNION ALL
-- SELECT Members.Name, Child.MemberName as [MemberName]
-- FROM Members
-- JOIN HierarchyData Child ON Members.MemberName = Child.Name
--)
--SELECT * FROM Members
--ORDER BY [Name], [MemberName]
答案 2 :(得分:1)
我使用以下代码模式来遵循SQL Server 2000中的层次结构。“魔术”是将深度值添加到临时表中,以便您可以在WHERE子句中使用它。
SET NOCOUNT ON
CREATE TABLE #super_trees
(
supervisor_uid INTEGER,
actor_uid INTEGER,
depth INTEGER
)
DECLARE
@more_users BIT,
@depth INTEGER
SET @more_users = 1
SET @depth = 0
INSERT INTO #super_trees VALUES (@supervisor_uid, @supervisor_uid, @depth)
SET @depth = @depth + 1
WHILE (@more_users = 1)
BEGIN
INSERT INTO #super_trees (supervisor_uid, actor_uid, depth)
SELECT u.supervisor_uid,
u.actor_uid,
@depth
FROM #super_trees sr
INNER JOIN
dbo.users u
ON (sr.actor_uid = u.supervisor_uid)
WHERE sr.depth = (@depth - 1)
IF @@ROWCOUNT < 1
SET @more_users = 0
SET @depth = @depth + 1
END
答案 3 :(得分:0)
使用上述CTE不符合海报的目的。他/她想要平整数据。 CTE仅返回ParentID列下具有不同值的层次结构信息。
名称会员名称 一个B. 一个C. B D. D E. F G
所以上面是你使用CTE,而不是
名称会员名称 一个B. 一个C. 广告 一个E. B D. B E. D E. F G
答案 4 :(得分:-1)
我建议你稍微改动一下你的数据。您没有记录表明A是层次结构的根。补充一点:
INSERT INTO #TMP(Name, MemberName) VALUES (NULL, 'A')
极大地简化了事情(通常,邻接列表将以“反过来”的方式表示:列Name
和列ParentName
,它们对应于您的MemberName
,Name
列。
使用该设置,您可以使用公用表表达式来完成这项工作:
WITH Node (Name, ParentName)
AS (
SELECT Name, ParentName
FROM Tab
WHERE ParentName IS NULL
UNION ALL
SELECT Tab.Name, Tab.ParentName
FROM Tab
INNER JOIN Node
ON ParentName = Node.Name
)
SELECT Name, ParentName
FROM Node
不幸的是,正如Hogan所指出的那样,MS SQL 2005中支持公共表表达式。