用于展平记录层次结构的 Sql 服务器查询
我有一个描述层次结构的表:
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 循环,直到交叉联接中没有更多适用记录可插入:
WHILE (@iRowsFound > 0)
BEGIN
INSERT INTO #TMP ([Name],[MemberName])
(
SELECT
[NewName] = ??,
[NewMember] = ??
FROM
[HierarchyData],[#TMP]
WHERE
???
)
SELECT @iRowsFound = @@ROWCOUNT
END
while 循环,将临时表交叉联接到 heirachy 表,并将 只是不确定我是否走在正确的轨道上,因为我对交叉连接选择应该是什么样子有点困惑。有没有人做过类似的事情(在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
I have a table that describes a hierarchy:
Name MemberName
A B
A C
B D
D E
F G
MemberName references the Name column of the same table. From this table, I can easily query to see that B and C are members within A, D is a member of B, E is a member of D and G is a member of F.
Based on this structure it's difficult to write a query that shows that D, and E are also indirectly a member of A. D and E are also indirectly a member of B, etc. So what I need to do is build up a new table that shows shows all the indirect members. So for the above table data, I'd end up with a table containing:
Name MemberName
A B
A C
A D
A E
B D
B E
D E
F G
I started by putting all the records that weren't members of other records (top level) records) into a temp table:
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
Then my theory was to, in a while loop, cross join the temp table to the heirachy table and insert the applicable records from the cross join back into the temp table, and perform that while loop until there were no more applicable records in the cross join to insert:
WHILE (@iRowsFound > 0)
BEGIN
INSERT INTO #TMP ([Name],[MemberName])
(
SELECT
[NewName] = ??,
[NewMember] = ??
FROM
[HierarchyData],[#TMP]
WHERE
???
)
SELECT @iRowsFound = @@ROWCOUNT
END
I'm just not sure I'm on the right track, as I'm a little stumped as to what the cross join select should look like. Has anyone done something like this (in sql server 2000)?
Edit: I think I may have gotten it: - Although I'm pretty sure there must be a
more efficient way to do this...
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
很遗憾你不在sql server 2005或更高版本上,使用递归CTE很容易,代码在这里:
在2000年,你可以基本上以相同的方式完成它(将最后一个选择的结果连接到原始表,直到你没有循环内最后一组的结果),但这要困难得多,因为您必须通过计数器跟踪您正在进行的迭代。恶心。
这有帮助吗,或者您想要一些 sql 2000 伪代码吗?
更好的是,只需升级!
So sad you are not on sql server 2005 or later, it is easy with a recursive CTE the code is here:
In 2000 you can do it basically the same way (joining the results of the last select to the original table till you have no results of the last set inside a loop), but it is much harder because you have to keep track of what iteration you are on via a counter. Yuck.
Does this help, or do you want some sql 2000 pseudo code?
Better yet, just upgrade!
这是 SQL 2000 版本。
一些注意事项:这适用于任意数量的级别,并且不会出现循环错误(就像 CTE 版本那样。)
Here is an SQL 2000 version.
Some notes: This will work with any numbers of levels and will not have cycle errors (like the CTE versions will.)
我使用以下代码模式来遵循 SQL Server 2000 中的层次结构。“神奇”是将深度值添加到临时表中,以便您可以在 WHERE 子句中使用它。
I've used the following code pattern to follow a hierarchy in SQL Server 2000. The "magic" is adding the depth value to the temporary table so you can use that in the WHERE clause.
使用上述 CTE 不符合海报的目的。他/她想要扁平化数据。 CTE 仅返回 ParentID 列下具有不同值的层次结构信息。
姓名 会员姓名
AB
交流电
BD
德
FG
所以上面是您使用 CTE 得到的结果,而不是
Name MemberName
AB
交流电
广告
AE
BD
是
德
FG
Using CTE above does not meet the objective of the poster. He/she wants to flatten the data. CTE only returns hierarchy information with different values under the ParentID column.
Name MemberName
A B
A C
B D
D E
F G
So the above is what you get using CTE, NOT
Name MemberName
A B
A C
A D
A E
B D
B E
D E
F G
我建议您对您的数据稍作修改。您没有记录表明 A 是层次结构的根。添加:
大大简化了事情(而且,通常情况下,邻接列表将以“相反的方式”表示:列
Name
和列ParentName
,它们对应于分别是您的MemberName
和Name
列通过该设置,您可以使用通用表表达式来完成这项工作:
不幸的是,通用表表达式是受支持的。正如 Hogan 所指出的,在 MS SQL 2005 及更高版本中。
I would advise you to make a slight alteration to your data. You do not have a record that says that A is the root of the hierarchy. Adding that:
greatly simplifies things (also, typically, the adjacency list would be represented 'the other way around': a column
Name
, and a columnParentName
which would correspond to yourMemberName
,Name
columns respectively.With that setup you can use a common table expression to do the job:
Unfortunately, common table expressions are supported in MS SQL 2005 and up as pointed out by Hogan.