原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://qianzhang.blog.51cto.com/317608/1205679
对于树/图的遍历,通常有2种算法来实现:迭代(Iteration)和递归(Recursion),迭代就是利用循环反复取值/赋值的过程;递归则是反复自己调用自己来获得最终结果。
不过SQL Server里的递归有32层嵌套限制,目的在于防止代码进入死循环。除非使用提示OPTION (MAXRECURSION 0)
测试数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
if OBJECT_ID(
'city'
)
is
not
null
drop
table
city
GO
create
table
city
(
id
int
,
name
nvarchar(10),
pid
int
,
depth
int
)
GO
insert
into
city
select
1,N
'江苏省'
,0,0
union
all
select
2,N
'南京市'
,1,1
union
all
select
3,N
'玄武区'
,2,2
union
all
select
4,N
'鼓楼区'
,2,2
union
all
select
5,N
'浙江省'
,0,0
union
all
select
6,N
'杭州市'
,5,1
union
all
select
7,N
'西湖区'
,6,2
union
all
select
8,N
'滨江区'
,6,2
union
all
select
9,N
'苏州市'
,1,1
union
all
select
10,N
'吴中区'
,9,2
union
all
select
11,N
'吴江区'
,9,2
|
一. 查找子节点
查找节点1的所有子节点,返回结果如下:
id | name | pid | depth |
1 | 江苏省 | 0 | 0 |
2 | 南京市 | 1 | 1 |
3 | 玄武区 | 2 | 2 |
4 | 鼓楼区 | 2 | 2 |
9 | 苏州市 | 1 | 1 |
10 | 吴中区 | 9 | 2 |
11 | 吴江区 | 9 | 2 |
1. 迭代
(1) 不借助depth,通过not in来向下查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
if OBJECT_ID(
'f_get_child'
)
is
not
null
drop
function
f_get_child
GO
create
function
f_get_child
(
@id
int
)
returns
@t
table
(id
int
)
as
begin
insert
into
@t
select
@id
--insert into @t select id from city where pid = @id
while @@ROWCOUNT>0
begin
insert
into
@t
select
a.id
from
city a
inner
join
@t b
on
a.pid = b.id
where
a.id
not
in
(
select
id
from
@t)
end
return
end
GO
select
*
from
city
where
id
in
(
select
id
from
f_get_child(1))
|
(2) 通过depth来逐层查找
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
if OBJECT_ID(
'f_get_child'
)
is
not
null
drop
function
f_get_child
GO
create
function
f_get_child
(
@id
int
)
returns
@t
table
(id
int
, depth
int
)
begin
declare
@depth
int
set
@depth = 0
insert
@t
select
ID,@depth
from
city
where
ID =@ID
while @@ROWCOUNT>0
begin
set
@depth = @depth + 1
insert
@t
select
a.ID,@depth
from
city a, @t b
where
a.pid = b.ID
and
b.depth = @depth - 1
end
return
end
GO
select
*
from
city
where
id
in
(
select
id
from
f_get_child(1))
|
2. 递归
(1) 自定义函数递归
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
if OBJECT_ID(
'f_get_child'
)
is
not
null
drop
function
f_get_child
GO
create
function
f_get_child
(
@id
int
)
returns
@t
table
(id
int
)
as
begin
declare
@pid
int
set
@pid =
null
insert
into
@t
select
@id
union
all
select
id
from
city
where
pid = @id
if exists(
select
1
from
city a
inner
join
@t b
on
a.pid = b.id
where
a.id
not
in
(
select
id
from
@t))
begin
insert
into
@t
select
a.id
from
city a
inner
join
@t b
on
a.pid = b.id
where
a.id
not
in
(
select
id
from
@t)
union
all
select
*
from
f_get_child(@pid)
end
return
end
GO
select
*
from
city
where
id
in
(
select
*
from
f_get_child(1))
|
(2) CTE递归
1
2
3
4
5
6
7
8
9
10
11
12
|
declare
@id
int
set
@id = 1;
with
tmp
as
(
select
*
from
city
where
id = @id
union
all
select
a.*
from
city a
inner
join
tmp b
on
a.pid = b.id
)
select
*
from
tmp
order
by
id
|
二. 查找父节点
查找节点8的所有父节点,返回结果如下:
id | name | pid | depth |
5 | 浙江省 | 0 | 0 |
6 | 杭州市 | 5 | 1 |
8 | 滨江区 | 6 | 2 |
1. 迭代
父节点只有一个,不需要做什么限制,一直往上级查找pid就可以了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
if OBJECT_ID(
'f_get_parent'
)
is
not
null
drop
function
f_get_parent
GO
create
function
f_get_parent
(
@id
int
)
returns
@t
table
(id
int
)
as
begin
declare
@pid
int
insert
into
@t
select
@id
select
@pid = pid
from
city
where
id = @id
while @pid<>0
begin
insert
into
@t
values
(@pid)
select
@pid=pid
from
city
where
id=@pid
end
return
end
GO
select
*
from
city
where
id
in
(
select
*
from
f_get_parent(8))
|
2. 递归
(1) 自定义函数递归
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
if OBJECT_ID(
'f_get_parent'
)
is
not
null
drop
function
f_get_parent
GO
create
function
f_get_parent(@id
int
)
returns
@t
table
(id
int
)
AS
begin
declare
@pid
int
select
top
1 @pid = pid
from
city
where
id = @id
if @pid <> 0
begin
insert
into
@t
select
@id
union
all
select
*
from
f_get_parent(@pid)
end
else
begin
insert
into
@t
select
@id
end
return
end
GO
select
*
from
city
where
id
in
(
select
*
from
f_get_parent(8))
|
(2) CTE递归
1
2
3
4
5
6
7
8
9
10
11
12
|
declare
@id
int
set
@id = 8;
with
tmp
as
(
select
*
from
city
where
id = @id
union
all
select
a.*
from
city a
inner
join
tmp b
on
a.id = b.pid
)
select
*
from
tmp
order
by
id
|