我在SQL Server 2008 R2中有两个表:
tblCategories
cat_id
cat_name
tblCategoryHierarchy
cat_parent_id
cat_child_id
每个类别下可以有任意数量的级别和任意数量的子类别。无法更改表结构。
我要做的是,提供一个cat_id
并在层次结构中返回所有cat_id
,无论有多少级别。
例如(tblCategoryHierarchy
):
cat_parent_id cat_child_id
101 200
101 201
101 202
101 203
202 300
202 301
如果我提供cat_id
101,我需要返回:200,201,202,300,301和203.
我用VB尝试过这个但是我有很多很多查询而且速度非常慢。所以我正在寻找一个纯粹的SQL解决方案,我希望它能够快速而不占用太多的服务器资源。
商店程序对我来说听起来不错,那么有没有一种方法可以达到我需要的方式..?
答案 0 :(得分:2)
您可以使用递归公用表表达式(CTE)执行此操作。
下面这样的事情应该这样做。这里X = 4是一个
常数:您的输入cat_id
(在您的示例中为200)。
WITH CatCTE (cat_id) AS
(
SELECT t.cat_id
FROM tblCategories t
WHERE t.cat_id = 4
UNION ALL
SELECT P.cat_child_id as cat_id
FROM CatCTE AS m
JOIN tblCategoryHierarchy AS P on m.cat_id = P.cat_parent_id
)
SELECT cat_id
FROM CatCTE
WHERE
cat_id <> 4;
SCRIPT创建了一些测试数据:
create table tblCategories(cat_id int, cat_name varchar(20));
create table tblCategoryHierarchy(cat_parent_id int, cat_child_id int);
insert into tblCategories(cat_id, cat_name) values ( 1, 'cat 1');
insert into tblCategories(cat_id, cat_name) values ( 2, 'cat 2');
insert into tblCategories(cat_id, cat_name) values ( 3, 'cat 3');
insert into tblCategories(cat_id, cat_name) values ( 4, 'cat 4');
insert into tblCategories(cat_id, cat_name) values ( 5, 'cat 5');
insert into tblCategories(cat_id, cat_name) values ( 6, 'cat 6');
insert into tblCategories(cat_id, cat_name) values ( 7, 'cat 7');
insert into tblCategories(cat_id, cat_name) values ( 8, 'cat 8');
insert into tblCategories(cat_id, cat_name) values ( 9, 'cat 9');
insert into tblCategories(cat_id, cat_name) values (10, 'cat 10');
insert into tblCategories(cat_id, cat_name) values (11, 'cat 11');
insert into tblCategories(cat_id, cat_name) values (12, 'cat 12');
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 1, 2);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 1, 3);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 4, 6);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 4, 8);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 8, 10);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 8, 11);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values (11, 12);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 5, 7);
insert into tblCategoryHierarchy (cat_parent_id, cat_child_id) values ( 5, 9);
答案 1 :(得分:0)
这应该对您有所帮助,获得所有层次结构一次将提供更好的性能,然后通过多次调用一次获得一个分支。
-- Here we will store the hierarchy
Dim parentToChild As New Dictionary(Of Integer, IList(Of Int))()
...
-- How you get to here depends on how you access your database,
-- somewhere between ADO.Net and EF.
-- The command simply retrieves all rows from [tblCategoryHierarchy]
-- Column 0 is [cat_parent_id], Column 1 is [cat_child_id]
Using reader As SqlDataReader = command.ExecuteReader()
While reader.Read()
Dim parent = reader.GetInt32(0)
If Not parentToChild.ContainsKey(parent) Then
parentToChild.Add(parent, New List(Of Integer))
End If
parentToChild(parent).Add(reader.GetInt32(1))
End While
End Using
...
通过这种方式,您可以将层次结构加载到字典中,以便快速查找。
下一步取决于你想对层次结构做些什么。
如果您使用此扩展方法
<Extension()>
Public Shared Function Traverse(Of MyNode)( _
ByVal root As MyNode,
ByVal childSelector As Function(Of MyNode, IEnumerable(Of MyNode))
As IEnumerable(Of MyNode)
Dim stack = New Stack(Of MyNode)()
stack.Push(root)
While stack.Count > 0
Dim current = stack.Pop()
Yield current
For Each Dim child In childSelector(current)
stack.Push(child)
Next
End While
End Function
你可以轻易地遍历200
的所有孩子,例如,通过这样做,
Dim children = 200.Traverse(Function(parent) parentToChild(parent))
For Each child In children
...
Next
如果层次结构经常更改,在多个线程上,或者您只需要层次结构的一个分支或谱系,请使用Peter Petrov's answer。