SQL Server中CTE的另一种递归方式-从底层向上递归

简介:
    SQL Server中的公共表表达式(Common Table Expression,CTE)提供了一种便利的方式使得我们进行递归查询。所谓递归查询方便对某个表进行不断的递归从而更加容易的获得带有层级结构的数据。典型的例子如MSDN(https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx)中提到的获取员工关系层级的结构,如图1所示。

image

图1.获取员工层级结构

 

    图1所示的例子是一个简单的通过递归获取员工层级的例子,主要理念是通过一个自连接的表(员工表,连接列为员工ID与其上司ID,没有上司的人为公司最大的CEO),不断递归,从而在每次递归时将员工层级+1,最终递归完成后最低级别的员工可以排出其在公司的层级,也就是如图1中所示的3。

 

    图1的例子应用场景比较广泛,网上也有很多文章提到过这种方式,但当我们需要另一种递归方式时,上面的例子就无能为力了。假设我们有这样一个需求,比如现在流程的微商传销的提成方式,假设员工分为3级,分别为一级代理、二级代理、最终销售。那么算业绩的时候可能是重复提成,比如一级代理提二级代理销售额的3%,一级代理提最终销售的1%。二级代理提最终销售的2%等等。那么我们需要从数据库中提取出所有代理的所有利润就不是一件容易的事。一个简单的示意图如图2所示:

image

图2.多层提成的模型

 

    而此时每一级代理自身又可以直接进行销售,所以代理的销售额并不简单等于其下级代理销售额的和,因此我们最简单的办法就是列出每个代理所有下属的代理,并将其销售额按照业务规则相乘即可。

    因此我们需要一个查询将每个代理以及其下属层级全部列出来。由于实际需求可能都是按照省份划分代理,比如广州省是一级,广州市是二级,下属天河区是三级。下面是我们测试数据用的表:

create table #tb(id varchar(3) , pid varchar(3) , name varchar(10))
insert into #tb values('1' , null  , '广东省')
insert into #tb values('2' , '1' , '广州市')
insert into #tb values('3' , '1' , '深圳市')
insert into #tb values('4' , '2' , '天河区')
insert into #tb values('5' , '3' , '罗湖区')
insert into #tb values('6' , '3' , '福田区')
insert into #tb values('7' , '3' , '宝安区')
insert into #tb values('8' , '7' , '西乡镇')
insert into #tb values('9' , '7' , '龙华镇')
代码清单1.测试数据

 

    而我们希望获得的数据类似:

image

图3.希望获得的数据

 

    在此,我们采用的策略不是与MSDN中的例子不同,而是自下而上递归。代码如代码清单2所示:

WITH    cte ( id, pid, NAME )
          AS ( SELECT   id ,
                        pid ,
                        name 
               FROM     #tb a
               WHERE    a.pid IS NOT NULL
               UNION ALL
               SELECT   b.id ,
                        a.pid ,
                        b.NAME
                    
               FROM     #tb a
                        INNER JOIN cte b ON a.id = b.pid
            WHERE a.pid IS NOT NULL
                        
             )
    SELECT  pid AS id,id AS SID,NAME
    FROM    cte a 
    UNION 
    SELECT id,id,name FROM #tb
    ORDER BY id,sid
代码清单2.从下而上的递归
 
    代码清单2展示了方案,与MSDN自顶向下的例子不同,我们这里采用了自下而上的递归,递归的终止条件是WHERE    a.pid IS NOT NULL,而不是a.pid IS  NULL,该条件使得先从底层开始递归,然后通过a.id = b.pid而不是a.pid=b.id使得查找的过程变为由子节点找父节点,从而实现了上述需求。


本文转自CareySon博客园博客,原文链接:http://www.cnblogs.com/CareySon/p/4531106.html如需转载请自行联系原作者
相关文章
SQL如何在CTE中使用Order By的功能
SQL Server如何在CTE中使用Order By的功能
|
SQL 关系型数据库 PostgreSQL
CTE vs 子查询:深入拆解PostgreSQL复杂SQL的隐藏性能差异
本文深入探讨了PostgreSQL中CTE(公共表表达式)与子查询的选择对SQL性能的影响。通过分析两者底层机制,揭示CTE的物化特性及子查询的优化融合优势,并结合多场景案例对比执行效率。最终给出决策指南,帮助开发者根据数据量、引用次数和复杂度选择最优方案,同时提供高级优化技巧和版本演进建议,助力SQL性能调优。
1561 1
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
SQL 存储 监控
SQL Server的并行实施如何优化?
【7月更文挑战第23天】SQL Server的并行实施如何优化?
1051 13
解锁 SQL Server 2022的时间序列数据功能
【7月更文挑战第14天】要解锁SQL Server 2022的时间序列数据功能,可使用`generate_series`函数生成整数序列,例如:`SELECT value FROM generate_series(1, 10)。此外,`date_bucket`函数能按指定间隔(如周)对日期时间值分组,这些工具结合窗口函数和其他时间日期函数,能高效处理和分析时间序列数据。更多信息请参考官方文档和技术资料。
741 9
|
SQL 存储 网络安全
关系数据库SQLserver 安装 SQL Server
【7月更文挑战第26天】
445 6
|
SQL Oracle 关系型数据库
MySQL、SQL Server和Oracle数据库安装部署教程
数据库的安装部署教程因不同的数据库管理系统(DBMS)而异,以下将以MySQL、SQL Server和Oracle为例,分别概述其安装部署的基本步骤。请注意,由于软件版本和操作系统的不同,具体步骤可能会有所变化。
1554 3
|
存储 SQL C++
对比 SQL Server中的VARCHAR(max) 与VARCHAR(n) 数据类型
【7月更文挑战7天】SQL Server 中的 VARCHAR(max) vs VARCHAR(n): - VARCHAR(n) 存储最多 n 个字符(1-8000),适合短文本。 - VARCHAR(max) 可存储约 21 亿个字符,适合大量文本。 - VARCHAR(n) 在处理小数据时性能更好,空间固定。 - VARCHAR(max) 对于大文本更合适,但可能影响性能。 - 选择取决于数据长度预期和业务需求。
1532 1
|
SQL 存储 测试技术
|
SQL 关系型数据库 分布式数据库
PolarDB产品使用问题之如何迁移SQL Server
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
362 6