在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,递归查询主要用于层次结构的查询,从叶级(Leaf Level)向顶层(Root Level)查询,或从顶层向叶级查询,或递归的路径(Path)。

一、递归查询

1.结构: CTE的递归查询必须满足三个条件:初始条件,递归调用表达式,终止条件,CTE 递归查询的伪代码如下:
 WITH cte_name ( column_name [,...n] ) AS(--定点成员CTE_query_definition UNION ALL--递归子查询成员CTE_query_definition )-- Statement using the CTESELECT * FROM cte_name

  第一个查询为定点成员:定点成员只是一个返回有效表的查询,用于递归的基础或定位点。

  第二个查询被称为递归子查询成员:该子查询调用CTE名称,触发递归查询,实际上是递归子查询调用递归子查询。

  在逻辑上可以将CTE名称的内部应用理解为前一个查询的结果集。

2.递归结束条件:

  递归查询没有显式的递归终止条件,只有当递归子查询返回空结果集(没有数据行返回)或是超出了递归次数的最大限制时,才停止递归。

  默认的递归查询次数是100,可以使用查询提示(hint):MAXRECURSION 控制递归的最大次数:OPTION( MAXRECURSION 16);如果允许无限制的递归次数,使用查询提示:option(maxrecursion 0);当递归查询达到指定或默认的 MAXRECURSION 数量限制时,SQL Server将结束查询并返回错误,如下:

 The statement terminated. The maximum recursion 10 has been exhausted before statement completion.

  事务执行失败,该事务包含的所有操作都被回滚。在产品环境中,慎用maxrecursion 查询提示,推荐通过 where 条件限制递归的次数。

3.递归步骤:
 step1:定点子查询设置CTE的初始值,即CTE的初始值Set0;递归调用的子查询过程:递归子查询调用递归子查询;step2:递归子查询第一次调用CTE名称,CTE名称是指CTE的初始值Set0,第一次执行递归子查询之后,CTE名称是指结果集Set1;step3:递归子查询第二次调用CTE名称,CTE名称是指Set1,第二次执行递归子查询之后,CTE名称是指结果集Set2;step4:在第N次执行递归子查询时,CTE名称是指Set(N-1),递归子查询都引用前一个递归子查询的结果集;Step5:如果递归子查询返回空数据行,或超出递归次数的最大限制,停止递归;
4.Sql递归的优点:

  效率高,大量数据集下,速度比程序的查询快。

5.递归查询的作用:

  用来查询指定成员及其递归成员(下属所有成员,包括下属的下属)

6.适用场景:

  成员权限查询、等级区域查询,其他相关类似查询。

二、实例Ⅰ – 员工职称

1,创建测试数据

  ManagerID是UserID的父节点,这是一个非常简单的层次结构模型。

CREATE TABLE dbo.dt_user (UserID INT, ManagerID INT, Name NVARCHAR(10));
INSERT INTO dbo.dt_user
SELECT 1, -1, N'Boss' UNION ALL
SELECT 11, 1, N'A1' UNION ALL
SELECT 12, 1, N'A2' UNION ALL
SELECT 13, 1, N'A3' UNION ALL
SELECT 111, 11, N'B1' UNION ALL
SELECT 112, 11, N'B2' UNION ALL
SELECT 121, 12, N'C1';
2,查询每个User的的直接上级Manager
WITH cte AS(SELECT UserID, ManagerID, name, name AS ManagerNameFROM   dbo.dt_userWHERE  ManagerID=-1UNION ALLSELECT c.UserID, c.ManagerID, c.Name, P.name AS ManagerNameFROM   cte PINNER JOIN dbo.dt_user c ON P.UserID=c.ManagerID)
SELECT UserID, ManagerID, Name, ManagerName FROM cte ORDER BY UserID;

  step1:查询ManagerID=-1,作为root node,这是递归查询的起始点。

  step2:迭代公式是 union all 下面的查询语句。在查询语句中调用中cte,而查询语句就是cte的组成部分,即 “自己调用自己”,这就是递归的真谛所在。所谓迭代,是指每一次递归都要调用上一次查询的结果集,Union ALL是指每次都把结果集并在一起。

  step3-N,迭代公式利用上一次查询返回的结果集执行特定的查询,直到CTE返回null 或达到最大的迭代次数。最终的结果集是迭代公式返回的各个结果集的并集,求并集是由Union All 子句定义的,并且只能使用Union ALL。

3,查询路径,在层次结构中查询子节点到父节点的path
WITH cte AS(SELECT UserID, ManagerID, name, CAST(name AS NVARCHAR(MAX)) AS ReportPathFROM   dbo.dt_userWHERE  ManagerID=-1UNION ALLSELECT c.UserID, c.ManagerID, c.Name, c.name+'->'+P.ReportPath AS ReportPathFROM   cte PINNER JOIN dbo.dt_user c ON P.UserID=c.ManagerID)
SELECT UserID, ManagerID, Name, ReportPath FROM cte ORDER BY UserID;

三、实例Ⅱ — 行政区划

1,需求模拟

  在TSQL中实现层次结构,例如有这样一种数据结构,省,市,县,乡,村,如何使用一张表表示这种数据结构,并且允许是不对称的,例如,上海市是个直辖市,没有省份。

 CREATE TABLE dbo.hierarchy (ID INT NOT NULL PRIMARY KEY,--Type int not null,ParentID INT NOT NULL,name VARCHAR(100) NOT NULL);

  type表示类型,可以设置:省,Type是1;市,type是2,以此类推。

  ParentID标识的是父级ID,例如信阳市的ParentID是河南省的ID。

2,插入测试数据
INSERT INTO dbo.hierarchy
VALUES(1, 0, '河南省'),(2, 1, '信阳市'),(3, 2, '淮滨县'),(4, 3, '芦集乡'),(12, 3, '邓湾乡'),(13, 3, '台头乡'),
(14, 3, '谷堆乡'),(8, 2, '固始县'),(9, 8, '李店乡'),(10, 2, '息县'),(11, 10, '关店乡'),(5, 1, '安阳市'),
(6, 5, '滑县'),(7, 6, '老庙乡'),(15, 1, '南阳市'),(16, 15, '方城县'),(17, 1, '驻马店市'),(18, 17, '正阳县');SELECT * FROM dbo.hierarchy ORDER BY ParentID;
3,实现由父级向子级的查询

  由于实际的数据可能有很多,所以,要想获取河南省下的所有市,县,乡,村等信息,必须使用递归查询

WITH cte(Id, ParentID, Name) AS(SELECT * FROM dbo.hierarchy WHERE id=1UNION ALLSELECT h.* FROM dbo.hierarchy h INNER JOIN cte c ON h.ParentID=c.id
--where c.id!=h.ID
)SELECT * FROM cte ORDER BY ParentID;

  如果要查看向内递归到多少level,可以使用派生列,level=0是省level,level=1是市level,依次类推。

WITH cte(Id, ParentID, Name, Level) AS(SELECT ID, ParentID, Name, 0 AS Level FROM dbo.hierarchy WHERE id=1UNION ALLSELECT h.ID, h.ParentID, h.Name, c.Level+1 AS LevelFROM   dbo.hierarchy hINNER JOIN cte c ON h.ParentID=c.id
--where c.id!=h.ID
)SELECT * FROM cte ORDER BY ParentID;

  查询结果如图:

4,由子级向父级的递归查询
WITH cte AS(SELECT ID, ParentID, name FROM dbo.hierarchy WHERE id=4 --芦集乡的IDUNION ALLSELECT h.ID, h.ParentID, h.nameFROM   dbo.hierarchy hINNER JOIN cte c ON h.id=c.ParentID)SELECT ID, ParentID, name FROM cte ORDER BY ParentID;

  查询结果如图:

四、实例Ⅲ — 账单流水

DECLARE @tb TABLE (Debtor REAL,Creditor REAL,Direction NVARCHAR(1),Remainder REAL)
INSERT INTO @tb(Direction,Remainder) VALUES ('借',84.9000)
INSERT INTO @tb(Debtor,Creditor) VALUES (3000.000,0.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,800.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,2284.9000)
INSERT INTO @tb(Debtor,Creditor) VALUES (1144.0000,0.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,1144.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (5000.0000,0.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,5000.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (436.0000,0.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,436.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,4000.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (5000.0000,0.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,960.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,800.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (800.0000,0.0000)
INSERT INTO @tb(Debtor,Creditor) VALUES (0.0000,40.0000)
WITH TempDCR AS(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS 'ID' ,Debtor,Creditor,Direction,Remainder FROM @tb)
, TempReCursion AS(
SELECT TOP 1 ID, Debtor,Creditor,Remainder,Direction FROM TempDCR
UNION ALL
SELECT a.ID,a.Debtor,a.Creditor,b.Remainder+a.Debtor-a.Creditor,Direction=CASE WHEN a.Debtor>0 THEN N'借' ELSE N'贷' END FROM TempDCR a JOIN TempReCursion b ON a.ID=b.ID+1
)SELECT Debtor,Creditor,Direction,Remainder FROM TempReCursion

五、实例四 —层级汇总

--测试数据
with area(id,"name",f_id,leve) as (select  1,'中国',0,1 union allselect  2,'湖北',1,2 union allselect  3,'武汉',2,3 union allselect  4,'云贵',1,2 union allselect  5,'云南',4,3 union allselect  6,'贵阳',4,3 union allselect  7,'云南子区',5,4 union allselect  8,'贵阳子区',6,4 union allselect  9,'蔡甸',2,3
), "table"(id,area_id,"money") as (select  1,3,10 union allselect  2,9,5 union allselect  3,7,20 union allselect  4,8,30
)
--使用cte递归求出每个节点的路径
,t(id,f_id,"name","level",fullpath) as (select a.id,a.f_id,a."name",a.leve,cast(a.id as varchar(max))from area awhere a.leve=1union allselect b.id,b.f_id,b."name",b.leve,t.fullpath+'->'+cast(b.id as varchar(max))from area binner join t on t.id=b.f_id
)
--汇总统计每个节点的金额
select t.id,t."name",t."level",sum(c."money") as "money"
from t
inner join t t1 on charindex(t.fullpath,t1.fullpath)=1
inner join "table" c on c.area_id=t1.id
group by t.id,t."name",t."level"
--having t."level"=2 --筛选出第二层级
--order by t.id

六、实例五 — 地区层级

  数据准备:

/*
test表
ID        地区ID
Name      地区名称
Main_ID   地区所属上级ID
Sign      地区等级 例如:福建-厦门-湖里 分别是 1,2,3
*/
SELECT     1003    ID,    '福建'    Name,    0    Main_ID,    1 Sign INTO test    union all
SELECT     1050    ,    '福州'    ,    1003    ,    2    union all
SELECT     1051    ,    '厦门'    ,    1003    ,    2    union ALL
SELECT     1375    ,    '思明'    ,    1051    ,    3    union all
SELECT     1382    ,    '海沧'    ,    1051    ,    3    union all
SELECT     1381    ,    '湖里'    ,    1051    ,    3    union all
SELECT     1374    ,    '集美'    ,    1051    ,    3    union all
SELECT     1373    ,    '同安'    ,    1051    ,    3    union all
SELECT     1380    ,    '翔安'    ,    1051    ,    3    union ALL
SELECT     667582720122    ,    '鼓楼'    ,    1050    ,    3    union all
SELECT     667582725528    ,    '台江'    ,    1050    ,    3    union all
SELECT     667582729587    ,    '仓山'    ,    1050    ,    3    union all
SELECT     667582732602    ,    '马尾'    ,    1050    ,    3    union all
SELECT     667582735385    ,    '晋安'    ,    1050    ,    3    union all
SELECT     667582738507    ,    '闽侯'    ,    1050    ,    3    union all
SELECT     667582742586    ,    '连江'    ,    1050    ,    3    union all
SELECT     667582745634    ,    '罗源'    ,    1050    ,    3    union all
SELECT     667582748358    ,    '闽清'    ,    1050    ,    3    union all
SELECT     667582751824    ,    '永泰'    ,    1050    ,    3    union all
SELECT     667582755215    ,    '平潭'    ,    1050    ,    3    union all
SELECT     667582760309    ,    '福清'    ,    1050    ,    3    union all
SELECT     667582764565    ,    '长乐'    ,    1050    ,    3

  实例:

/*
查询:福建省(ID 1003)及其底下的所有地区
*/
WITH CTE AS
(
--父项
SELECT ID,Main_ID
FROM test WHERE ID=1003
UNION ALL
--递归结果集中的下级
SELECT a.ID,a.Main_ID
FROM test a
INNER JOIN CTE b ON b.ID=a.Main_ID
)SELECT a.*
FROM Test a
INNER JOIN CTE t ON a.ID=t.ID

  查询结果:

SQLServer CTE 递归查询相关推荐

  1. SQL Server CTE 递归查询全解

    在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,本文详细介绍CTE递归调用的特性和使用示例 ...

  2. SQLServer2005中的CTE递归查询得到一棵树

    最近研究了一下CTE递归查询,感觉这个CTE递归查询蛮好用的,在网上找到了一个比较好的例子,测试例子如下 1 use City; 2 go 3 create table Tree 4 ( 5 ID i ...

  3. SqlServer中递归查询父节点及其所属子节点

    SqlServer中递归查询父节点及其所属子节点 需求场景 SQL脚本实现-根据子节点查询所有的父节点 查询结果 SQL脚本实现-根据父节点查询所有的子节点 查询结果 需求场景 递归查询父节点及其所属 ...

  4. sqlserver cte

    在TSQL脚本中,也能实现递归查询,SQL Server提供CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询,本文详细介绍CTE递归调用的特性和使用示例 ...

  5. mysql cte递归_SQLSERVER中CTE语句结构及CTE递归查询

    SQL SERVER中CTE语句结构及CTE递归查询 CTE语句结构 公用表表达式 (CTE) 可以认为是在单个 SELECT.INSERT.UPDATE.DELETE 或 CREATE VIEW 语 ...

  6. SQL的CTE递归查询

    格式 以;with cte开头 (定义哪些列)as (查询的顶层 union all 内链接CTE查询 循环) 以下是 sunlike ERP系统的BOM表递归查询实例 ;with cte(hb,pa ...

  7. [SQL Server] TSQL实现SQL Server中CTE 递归查询

    参考博客:https://www.cnblogs.com/ljhdo/p/4580347.html 简介 递归查询主要应用于层级结构表的查询: 叶节点-> 根节点的查询 根节点-> 叶节点 ...

  8. 简单明了学习SQL CTE递归查询

    http://www.cnblogs.com/nokiaguy/archive/2009/02/01/1382207.html 转载于:https://www.cnblogs.com/ljf96345 ...

  9. mysql cte递归_CTE 递归查询全解

    TSQL脚本能实现递归查询,用户使用共用表表达式 CTE(Common Table Expression),只需要编写少量的代码,就能实现递归查询.本文详细介绍CTE递归调用的特性和使用示例,递归查询 ...

  10. sqlserver迁移到mysql遇到的那些坑

    背景 由于各种原因,成本啊.扩展性等,公司决定把线上的业务从sql server迁移到mysql RDS. 迁移过程主要包括了程序修改和数据库的迁移.程序修改我们略过不谈,我们重点关注数据库迁移. 大 ...

最新文章

  1. 【转】python编码大坑详解2
  2. Android中用 adb 命令操作数据库
  3. 欢乐纪中某B组赛【2019.1.18】
  4. nginx 反向proxy多个 tomcat,负载均衡
  5. 统计学第一章--最小二乘拟合正弦函数,正则化
  6. RESTful JSON Web服务最佳实践
  7. 学习HTML5+CSS3的第一天
  8. C++_类和对象_C++运算符重载_函数调用运算符重载_---C++语言工作笔记060
  9. PHP加密解密函数之Crypt
  10. Linux中各种锁原理概述
  11. esp8266网页控制RGB灯颜色
  12. 10个屌炸天的设计网址导航带你嗨翻科技设计界 #精选前端开发设计素材
  13. off邮箱服务器怎么看,没有和账号服务器连接 Kick Off 怎么办?
  14. 计算机硬盘更换图解,GHOST磁盘克隆详细图文教程,看后你也可以自己快速更换电脑硬盘...
  15. 基于 Spark 的文本情感分析
  16. Java_实现身份证信息提取个人信息
  17. 无领导小组讨论题目分类
  18. 个人财务流水账系统c语言,微易个人财务收支管理系统的教程
  19. www-authenticate
  20. Excel转Html(一)-样式转化-CSS-border-style-属性

热门文章

  1. 新手好例子图书馆管理系统Python+MySQL+tkinter图形化界面+源码(注释详细)
  2. 网络流-一江春水向东流
  3. flink流处理示例开发
  4. 播布客的视频讲座-下载(持续更新2011-12-31)
  5. *item_search_similar - 搜索相似的商品**
  6. 使用AccessibilityService(无障碍服务)自动完成手机设置功能
  7. 《指导生活的算法》读后感
  8. java日期计算天数_用Java计算两个日期之间的天数
  9. LG-P3939 数颜色
  10. url data 模式(url scheme data)