【原】SQL Server中使用CTE递归查询
目录
- 背景
- 问题
- 思路
- CTE
- CTE递归查询
- 结束语
- 参考资料
背景
好久未写博了,最近遇到一个问题:“怎么遍历出一个父级菜单下所有子菜单?”小的随后用CTE递归查询解决此问题,整理记录下来以作分享。
问题
怎么遍历出一个父级菜单下所有子菜单?
思路
- 用CTE递归查询解决
- 写一个自定义函数/存储过程迭代算法来实现
CTE
- 定义
- 语法结构
- 使用CTE准则
- 示例代码
定义
CTE(Common Table Expressions)是从SQL Server 2005以后版本才有的。指定的临时命名结果集,这些结果集称为CTE。 与派生表类似,不存储为对象,并且只在查询期间有效。与派生表的不同之处在于,CTE 可自引用,还可在同一查询中引用多次。使用CTE能改善代码可读性,且不损害其性能。
语法结构
CTE 的基本语法结构如下:
WITH expression_name [ ( column_name [,...n] ) ]
AS
( CTE_query_definition )
只有在查询定义中为所有结果列都提供了不同的名称时,列名称列表才是可选的。
运行 CTE 的语句为:
SELECT <column_list>
FROM expression_name;
使用CTE准则
创建CTE时,需要注意的一些准则,详见MSND列出的使用准则:
- 定义CTE时最好加前缀”;”
- CTE内部定义的列字段要保持一致
- CTE with之后第一句必须使用CTE的select。即CTE的生命周期只是在第一次使用之后就消亡。
- sp中只能使用一次with语句。
- 定义多个CTE时,只声明一个with关键字就行,比如
with test1
as
(
select * …………
),
test2 as
(
select * …………
)
通用表表达式(CTE)是SQL Server的一项新功能。本质上CTE是一个临时结果集,它仅仅存在于它发生的语句中。可以在SELECT、INSERT、DELETE或CREATE VIEW语句中建立一个CTE
示例代码
创建测试表
1 2 3 4 5 6 7 8 9 10 11 12 | USE MASTER GO IF EXISTS( SELECT * FROM SYSOBJECTS WHERE NAME = 'NODE' ) DROP TABLE NODE ---创建表 CREATE TABLE NODE ( NID INT PRIMARY KEY , PARENTID INT NOT NULL , NAME NVARCHAR(50) NOT NULL ) |
插入数据
1 2 3 4 5 6 7 8 9 10 11 | INSERT INTO NODE VALUES (1,0, 'aa' ), (2,1, 'bb' ), (3,2, 'cc' ), (4,2, 'dd' ), (5,3, 'ee' ), (6,4, 'ff' ), (7,5, 'gg' ), (8,4, 'hh' ), (9,3, 'ii' ), (10,5, 'jj' ); |
创建一个CTE的DEMO
;WITH NodeCTE(NID,PARENTID,NAME)
AS
(
SELECT NID,PARENTID,NAME FROM NODE WHERE NAME='BB'
UNION ALL
SELECT B.NID,B.PARENTID,B.NAME FROM NodeCTE A ,NODE B WHERE B.PARENTID=A.NID
)
SELECT * FROM NodeCTE;
CTE递归查询
- CTE递归查询结构
- CTE递归查询原理
- 原节点图和执行CTE结果集
CTE递归查询结构(引自MSDN)
Transact-SQL 中的递归 CTE 的结构与其他编程语言中的递归例程相似。尽管其他语言中的递归例程返回标量值,但递归 CTE 可以返回多行。
递归 CTE 由下列三个元素组成:
例程的调用。
递归 CTE 的第一个调用包括一个或多个由 UNION ALL、UNION、EXCEPT 或 INTERSECT 运算符联接的 CTE_query_definitions。由于这些查询定义形成了 CTE 结构的基准结果集,所以它们被称为“定位点成员”。
CTE_query_definitions 被视为定位点成员,除非它们引用了 CTE 本身。所有定位点成员查询定义必须放置在第一个递归成员定义之前,而且必须使用 UNION ALL 运算符联接最后一个定位点成员和第一个递归成员。
例程的递归调用。
递归调用包括一个或多个由引用 CTE 本身的 UNION ALL 运算符联接的 CTE_query_definitions。这些查询定义被称为“递归成员”。
终止检查。
终止检查是隐式的;当上一个调用中未返回行时,递归将停止。
注意
如果递归 CTE 组合不正确,可能会导致无限循环。例如,如果递归成员查询定义对父列和子列返回相同的值,则会造成无限循环。在测试递归查询的结果时,可以通过在 INSERT、UPDATE、DELETE 或 SELECT 语句的 OPTION 子句中使用 MAXRECURSION 提示和 0 到 32,767 之间的值,来限制特定语句允许的递归级数。
CTE递归查询原理
- 将CTE表达示拆分为“定位点成员”和“递归成员”
- 运行定位点成员,执行创建第一个结果集T0
- 运行递归成员时,将前一个结果集作为输入(Ti),将Ti+1作为输出
- 重复第三步,直到返回空集
- 返回结果集,通过UNION ALL合并T0 到 Tn的结果
原节点图和执行CTE结果集
CTE结果集
结束语
本文用CTE递归方式解决了我之前的问题,到这就要结束了。由于个人经验和能力原因,文中若有考虑不周或误导大家的地方请大牛们不吝指教,我会及时改正,谢谢!
参考资料
-
中文MSDN库之使用CTE的递归查询:http://msdn.microsoft.com/zh-cn/library/ms186243(v=SQL.100).aspx
-
中文MSDN库之CTE:http://msdn.microsoft.com/zh-cn/library/ms175972(SQL.100).aspx
-
巧用 DB2 递归 SQL:http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-1010liush/index.html
-
数据点: 通用表表达式:http://msdn.microsoft.com/zh-cn/magazine/cc163346.aspx#S2
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· SQL Server 2025 AI相关能力初探
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库