分享

SQL

 joey_nj 2017-05-30
SQL-图,树,层次结构和递归查询
树:指二叉, 三叉树,属于有向无循环图, 且根结点只有一个
层次结构: 如料表, 属于有向无循环图DAG, 不过和树不同, 根结点可以是一个或多个
图: 通用概念, 主要是看是否有循环, 循环的如道路系统,即无向循环图
案例一:返回指定结点的所有下属(员工系统,树)
With SubsCTE
As
(
--定位点成员, 返回指定的根结点
Select empid, empname, 0 as Lvl
From dbo.Employees
Where empid=@root
www.
Union All
--递归成员, 返回下级员工
Select C.empid, C.empname, P.lvl+1
From SubsCTE as P
Join dbo.Employees as C on C.managerID = P.empid
)
Select * from SubsCTE;
案例二: 返回指定结点的所有祖先
With MangersCTE
As
(
Select empid, managerid, empname, 0 as lvl
From dbo.Employees
Where empid = @empid
Union All
Select p.empid, p.ManagerID, p.empname, C.lvl+1
From ManagersCTE as C
Join dbo.Employees as P on C.managerid = p.empid
)
案例三: 返回带有路径的员工链
With SubsCTE
As
(
--定位点成员, 返回指定的根结点
Select empid, empname, 0 as Lvl, ('.' +Cast(empid as varchar(10)) + '.')) as Path
From dbo.Employees www.
Where empid=@root
Union All
--递归成员, 返回下级员工
Select C.empid, C.empname, P.lvl+1 , (P.path +Cast(C.empid as varchar(10))+'.') as Path
From SubsCTE as P
Join dbo.Employees as C on C.managerID = P.empid
)
Select * from SubsCTE;
案例四: 检测员工管理关系是否包含循环关系:1->3->1视为循环
思路: 采用员工路径链进行判断, 若包含现有的员工编号则视为循环, 添加循环标识cycle列
With SubsCTE
As
(
--定位点成员, 返回指定的根结点
Select empid, empname, 0 as Lvl, ('.' +Cast(empid as varchar(10)) + '.')) as Path,
--根结点不存在循环
0 as cycle
From dbo.Employees
Where empid=@root
Union All
--递归成员, 返回下级员工
Select C.empid, C.empname, P.lvl+1 , (P.path +Cast(C.empid as varchar(10))+'.') as Path,
--如果父路径包含子级ID则检测到循环
Case When P.Path like '%.'+Cast(c.empid as varchar(10)) +'.%' then 1 else 0 as cycle
From SubsCTE as P www.
Join dbo.Employees as C on C.managerID = P.empid
And p.cycle = 0--为防止死循环, 对已经检测出有循环的就不再进行循环了
)
Select * from SubsCTE where cycle=1;--仅返回有循环的路径行
案例五: 无向图返回最短路径, 以道路系统为例
Roads表结构:City1, City2, Distance(距离)--表示城市1与城市2的距离
思路: 先将无向道路图转换成有向图, 将一行变成两行, 再递归查询所有从出发城市能够到达的城市路径和距离,查询过程中为避免死循环, 应对循环做出判断, 当路径包含子路径时即不再循环, 再按照出发城市,到达城市进行分组, 获取最小路径, 最后再选择出所有出发城市, 到达城市和最短路径。
With Roads2
As
(
--将无向图变为有向图, 每行产生两行
Select city1 as From_City, city as To_City, distance from dbo.Roads
Union all
Select city2, city1, distance from dbo.Roads
),
RoadsPath as
(
--先选择所有出发和到达城市
Select from_city, to_city, distance,
Cast('.' + from_city +'.' + to_City +'.' as varchar(max)) as Path
From Roads2
Union all
--再递归执行已到达城市所能到达的后续城市
Select F.from_city, T.to_city, F.distance + T.distance,
Cast(F.path +T.to_city +'.' as varchar(max)) as Path
From RoadPaths as F
Join Roads2 as T
--递归死循环判断, 当父级路径已经包含能到达的城市时, 不再进行递归循环
On (Case When F.path like '%.' + T.to_city +'.%' then 1 else o end)=0
And F.to_city = T.from_city www.
),
RoadsMinDist --用于选取出最短路径
As
(
Select from_city, to_city, Min(distance) as minDist
From RoadPaths
Group by from_city, to_city
)
Select RP.*
From RoadMinDist as RMD
Inner join RoadPaths as RP
On RMD.from_city = RP.from_city
And RMD.to_city = RP.to_city
And RMD.MinDist= RP.distance
作者 Chao Hong

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多