SQL 递归查询初体验

目录

需求:

有如下一张职员上司表,要求给一个职工名,返回他直接或间接管理的所有职工名,写一个函数实现。

分析:

员工的下属也可能是其他员工的上司,即下属也有下属,这样一直找,直到某员工没有下属为止。SQL SERVER 从 2005 起,开始支持 CTE 表达式,可以帮助我们更好的处理这样的一类递归问题。为了体现出递归的简洁,我们先给出非递归即迭代解法:

if object_id('dbo.findEmp','TF') is not null
	drop function dbo.findEmp
go
/*寻找直接或间接下属*/
create function findEmp
(
@mgr varchar(20)
)
returns @empl table(name varchar(20)) -- 所有结果的union all
as
begin
	declare @newemp table(name varchar(20)) -- 新产生的结果 (或上次递归的结果)
	declare @temp table(name varchar(20))   -- 临时结果    (本次递归的结果)

	-- 第一级
	insert into @newemp
		select employee_name from manager
		where manager_name = @mgr

	while exists(select 1 from @newemp)
		begin
			insert into @empl
				select name from @newemp
			--第N级(第N次迭代时)
			insert into @temp
				select a.employee_name
				from manager a inner join @newemp b
				on a.manager_name = b.name
			except  -- 管理关系存在的循环时(非正常情况)也能正常工作
				select name from @empl

			delete from @newemp
			insert into @newemp select * from @temp
			delete from @temp
		end
	return
end

/*
-- 测试
select * from dbo.FindEmp('Jones')

name
--------------------
Duarte
Estovar
Basinsky
Corbin
Alon

(5 行受影响)
*/

再看看sql递归查询是如何实现的:

if object_id('dbo.findEmp_Plus') is not null
	drop function dbo.findEmp_Plus
go

create function findEmp_Plus
(
	@mgr varchar(20)
)
returns table
as
return
(
	with emp as
	(
		select employee_name
		from manager
		where manager_name = @mgr
	union all
		select b.employee_name
		from emp a inner join manager b
		on a.employee_name = b.manager_name
	)
	select * from emp
)
go

/*
-- 测试
select * from dbo.FindEmp_plus('Jones')

name
--------------------
Duarte
Estovar
Basinsky
Corbin
Alon

(5 行受影响)
*/

是不是直觉递归简洁巧妙多了!!!

递归查询是很简洁,但不太好理解,其实可以借助上面的迭代来理解,就本例求Jones的所有下属而言:

第一步生成cte中的原始数据,即Jones的直接下属,Duarte 和 Estovar;

第二步生成 Duarte 和 Estovar 的直接下属,Basinsky 和 Corbin;

第三步生成 Basinsky 的直接下属Alon;

第四步结果为空,递归结束,结果是各步生成数据的union.

更多例子,请看我的另一篇文章:SQL 递归查询实战

官方介绍,请看MSDN文档:使用公用表表达式的递归查询 (含执行过程举例解析)