递归CTE是由两个最小查询构建的.第一个是定位成员(Anchor Member,AM),它是一个非递归查询,第二个是递归成员(Recursive Member,RM),它是递归查询.在CTE括号中(AS 子句之后),定义独立查询或引用回相同CTE的查询,AM与RM由UNION ALL语句分隔.AM紧被调用一次,RM将被重复调用,直到查询不在返回数据行为止.可以使用UNION或UNION ALL运算符彼此追加多个AM,具体取决于是否希望删除重复数据(必须使用UNION ALL运算符来追加递归成员).语法如下:
实例:我们将创建一个员工表和一个名为ReportsTo的自引用字段,其引用回Emloyee_ID,然后编写一个查询,其返回像Stephen(Employee_ID=2)报告的所有员工以及向Stephen的下属报告的所有员工.
代码清单:
查询结果:
递归过程开始于Employee_ID=2的位置(定义成员或第一个SELECT).它获取该记录,并使用递归递归成员(UNION ALL之后的SELECT)获取所有向Stephen报告的记录以及该记录的子记录.(Goksin向Malek报告,Malek向Stephen报 告).每一个后续递归都尝试找到更多的子记录.它们以先前递归已经找到的员工作为父记录.最终该递归不返回结果,这是导致递归停止的原因(为什么没有返回 Kimberly的原因).如果该定位成员被改变成Employee_ID=1,则Kimberly也将在结果中被返回.
根据设计,递归成员将一直查找子记录,并可以无限循环.如果您怀疑将会进行许多循环,并希望限制递归调用次数,可以使用OPTION子句在外部查询的后面指定MAXRECURSION选项.
OPTION(MAXRECURSION 25)
这一选项将使sql server2005在cte出现指定限制时产生一条错误.默认情况下,限制为100(即省略该选项时).要不想指定选项.必须将MAXRECURSION设为0.
代码清单:
结果如下:
还会看到以下错误:
消息 530,级别 16,状态 1,第 1 行
语句被终止。完成执行语句前已用完最大递归 2。
一种可以避免出现此异常的方法是使用一个所产生的列来跟踪所在的级别,并包含在WHERE子句中,而不是MAXRECURSION.以下结果与上一示例相同,但不产生错误.
代码清单:
ok,CTE递归查询完成了
-------------------------------------
示例:
BOM按节点排序应用实例 -------------------------------------------------------------------------- -- Author : htl258(Tony) -- Date : 2010-04-23 02:37:28 -- Version:Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86) -- Jul 9 2008 14:43:34 -- Copyright (c) 1988-2008 Microsoft Corporation -- Developer Edition on Windows NT 5.1 <X86> (Build 2600: Service Pack 3) -- Subject: BOM按节点排序应用实例 -------------------------------------------------------------------------- --实例1: --> 生成测试数据表:tb IF NOT OBJECT_ID('[tb]') IS NULL DROP TABLE [tb] GO CREATE TABLE [tb]([id] INT,[code] NVARCHAR(10),[pid] INT,[name] NVARCHAR(10)) INSERT [tb] SELECT 1,'01',0,N'服装' UNION ALL SELECT 2,'01',1,N'男装' UNION ALL SELECT 3,'01',2,N'西装' UNION ALL SELECT 4,'01',3,N'全毛' UNION ALL SELECT 5,'02',3,N'化纤' UNION ALL SELECT 6,'02',2,N'休闲装' UNION ALL SELECT 7,'02',1,N'女装' UNION ALL SELECT 8,'01',7,N'套装' UNION ALL SELECT 9,'02',7,N'职业装' UNION ALL SELECT 10,'03',7,N'休闲装' UNION ALL SELECT 11,'04',7,N'西装' UNION ALL SELECT 12,'01',11,N'全毛' UNION ALL SELECT 13,'02',11,N'化纤' UNION ALL SELECT 14,'05',7,N'休闲装' GO --SELECT * FROM [tb] -->SQL查询如下: ;WITH T AS ( SELECT CAST(CODE AS VARCHAR(20)) AS CODE,*, CAST(ID AS VARBINARY(MAX)) AS px FROM tb AS A WHERE NOT EXISTS(SELECT * FROM tb WHERE id=A.pid) UNION ALL SELECT CAST(B.CODE+A.CODE AS VARCHAR(20)),A.*, CAST(B.px+CAST(A.ID AS VARBINARY) AS VARBINARY(MAX)) FROM tb AS A JOIN T AS B ON A.pid=B.id ) SELECT Code,Name FROM T ORDER BY px /* Code Name -------------------- ---------- 01 服装 0101 男装 010101 西装 01010101 全毛 01010102 化纤 010102 休闲装 0102 女装 010201 套装 010202 职业装 010203 休闲装 010204 西装 01020401 全毛 01020402 化纤 010205 休闲装 (14 行受影响) */ --实例2: --> 生成测试数据表:tb IF NOT OBJECT_ID('[tb]') IS NULL DROP TABLE [tb] GO CREATE TABLE [tb]([id] INT,[parentid] INT,[categoryname] NVARCHAR(10)) INSERT [tb] SELECT 1,0,'test1' UNION ALL SELECT 2,0,'test2' UNION ALL SELECT 3,1,'test1.1' UNION ALL SELECT 4,2,'test2.1' UNION ALL SELECT 5,3,'test1.1.1' UNION ALL SELECT 6,1,'test1.2' GO --SELECT * FROM [tb] -->SQL查询如下: ;WITH T AS ( SELECT *,CAST(ID AS VARBINARY(MAX)) AS px FROM tb AS A WHERE NOT EXISTS(SELECT * FROM tb WHERE id=A.[parentid]) UNION ALL SELECT A.*,CAST(B.px+CAST(A.ID AS VARBINARY) AS VARBINARY(MAX)) FROM tb AS A JOIN T AS B ON A.[parentid]=B.id ) SELECT [id],[parentid],[categoryname] FROM T ORDER BY px /* id parentid categoryname ----------- ----------- ------------ 1 0 test1 3 1 test1.1 5 3 test1.1.1 6 1 test1.2 2 0 test2 4 2 test2.1 (6 行受影响) */