SQL Server2005杂谈(2):公用表表达式(CTE)的递归调用

本文为原创,如需转载,请注明作者和出处,谢谢!

上一篇:SQL Server2005杂谈(1):使用公用表表达式(CTE)简化嵌套SQL

先看如下一个数据表(t_tree):

    上图显示了一个表中的数据,这个表有三个字段:id、node_name、parent_id。实际上,这个表中保存了一个树型结构,分三层:省、市、区。其中id表示当前省、市或区的id号、node_name表示名称、parent_id表示节点的父节点的id。
    现在有一个需求,要查询出某个省下面的所有市和区(查询结果包含省)。如果只使用SQL语句来实现,需要使用到游标、临时表等技术。但在SQL Server2005中还可以使用CTE来实现。

    从这个需求来看属于递归调用,也就是说先查出满足调价的省的记录,在本例子中的要查“辽宁省”的记录,如下:

id  
node_name   parent_id

1    
辽宁省        0

    然后再查所有parent_id字段值为1的记录,如下:

id  
node_name   parent_id

2     
沈阳市       1

3     
大连市       1

    最后再查parent_id字段值为2或3的记录,如下:

id   
node_name    parent_id

4      
大东区        2

5      
沈河区        2

6      
铁西区        2

    将上面三个结果集合并起来就是最终结果集。

    上述的查询过程也可以按递归的过程进行理解,即先查指定的省的记录(辽宁省),得到这条记录后,就有了相应的id值,然后就进入了的递归过程,如下图所示。

    从上面可以看出,递归的过程就是使用union all合并查询结果集的过程,也就是相当于下面的递归公式:

    resultset(n)
= resultset(n-1) union all current_resultset

    其中resultset(n)表示最终的结果集,resultset(n - 1)表示倒数第二个结果集,current_resultset表示当前查出来的结果集,而最开始查询出“辽宁省”的记录集相当于递归的初始条件。而递归的结束条件是current_resultset为空。下面是这个递归过程的伪代码:

 

public resultset getResultSet(resultset)
{
    if(resultset is null)
    {
        current_resultset =第一个结果集(包含省的记录集)
        将结果集的id保存在集合中
        getResultSet(current_resultset)
    }
    current_resultset = 根据id集合中的id值查出当前结果集
    if(current_result is null) return resultset
    将当前结果集的id保存在集合中
    return  getResultSet(resultset union all current_resultset)
}

// 获得最终结果集
resultset = getResultSet(null)

    从上面的过程可以看出,这一递归过程实现起来比较复杂,然而CTE为我们提供了简单的语法来简化这一过程。
    实现递归的CTE语法如下:

 

[ WITH <common_table_expression> [ ,n ] ]
<common_table_expression>::=
        expression_name [ ( column_name [ ,n ] ) ]
    AS (
       CTE_query_definition1  --  定位点成员(也就是初始值或第一个结果集) 
       union all
       CTE_query_definition2  --  递归成员
    )

    下面是使用递归CTE来获得“辽宁省”及下面所有市、区的信息的SQL语句:

 

with
district as 
(
    --  获得第一个结果集,并更新最终结果集
    select * from t_tree where node_name= N'辽宁省'
    union all
    --  下面的select语句首先会根据从上一个查询结果集中获得的id值来查询parent_id         
    --  字段的值,然后district就会变当前的查询结果集,并继续执行下面的select 语句
    --  如果结果集不为null,则与最终的查询结果合并,同时用合并的结果更新最终的查
    --  询结果;否则停止执行。最后district的结果集就是最终结果集。
    select a.* from t_tree a, district b
               where a.parent_id = b.id
)
select * from district

    查询后的结果如下图所示。

    下面的CTE查询了非叶子节点:

 

with
district as 
(
    select * from t_tree where node_name= N'辽宁省'
    union all
    select a.* from t_tree a, district b
               where a.parent_id = b.id
),
district1 as
(
    select a.* from district a where a.id in (select parent_id from district)    
)
select * from district1

    查询结果如下图所示。

    注:只有“辽宁省”和“沈阳市”有下子节点。

    在定义和使用递归CTE时应注意如下几点:

1. 递归 CTE 定义至少必须包含两个 CTE 查询定义,一个定位点成员和一个递归成员。可以定义多个定位点成员和递归成员;但必须将所有定位点成员查询定义置于第一个递归成员定义之前。所有 CTE 查询定义都是定位点成员,但它们引用 CTE 本身时除外。
2. 定位点成员必须与以下集合运算符之一结合使用:UNION ALL、UNION、INTERSECT 或 EXCEPT。在最后一个定位点成员和第一个递归成员之间,以及组合多个递归成员时,只能使用 UNION ALL 集合运算符。
3. 定位点成员和递归成员中的列数必须一致。
4. 递归成员中列的数据类型必须与定位点成员中相应列的数据类型一致。
5. 递归成员的 FROM 子句只能引用一次 CTE
expression_name。
6. 在递归成员的 CTE_query_definition 中不允许出现下列项:

(1)SELECT DISTINCT

(2)GROUP BY

(3)HAVING

(4)标量聚合

(5)TOP

(6)LEFT、RIGHT、OUTER JOIN(允许出现 INNER JOIN)

(7)子查询

(8)应用于对 CTE_query_definition 中的 CTE 的递归引用的提示。

7. 无论参与的 SELECT 语句返回的列的为空性如何,递归 CTE 返回的全部列都可以为空。
8. 如果递归 CTE 组合不正确,可能会导致无限循环。例如,如果递归成员查询定义对父列和子列返回相同的值,则会造成无限循环。可以使用 MAXRECURSION 提示以及在 INSERT、UPDATE、DELETE 或 SELECT 语句的 OPTION 子句中的一个 0 到 32,767 之间的值,来限制特定语句所允许的递归级数,以防止出现无限循环。这样就能够在解决产生循环的代码问题之前控制语句的执行。服务器范围内的默认值是 100。如果指定 0,则没有限制。每一个语句只能指定一个 MAXRECURSION 值。
9. 不能使用包含递归公用表表达式的视图来更新数据。
10. 可以使用 CTE 在查询上定义游标。递归 CTE 只允许使用快速只进游标和静态(快照)游标。如果在递归 CTE 中指定了其他游标类型,则该类型将转换为静态游标类型。
11. 可以在 CTE 中引用远程服务器中的表。如果在 CTE 的递归成员中引用了远程服务器,那么将为每个远程表创建一个假脱机,这样就可以在本地反复访问这些表。

下一篇:SQL Server2005杂谈(3):四个排名函数(row_number、rank、dense_rank和ntile)的比较

国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

时间: 2024-09-29 16:24:22

SQL Server2005杂谈(2):公用表表达式(CTE)的递归调用的相关文章

T-SQL查询进阶:详解公用表表达式(CTE)

简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的.但是,视图是作为系统对象存在数据库中,那对于结果集仅仅需要在存储过程或是用户自定义函数中使用一次的时候,使用视图就显得有些奢侈了. 公用表表达式(Common Table Expression)是SQL SERVER 2005版本之后引入的一个特性.CTE可以看作是一个临时的结果集,可以在接下来的一个SELECT,INSERT,UPDATE,DEL

SQL2005 学习笔记 公用表表达式(CTE)_mssql2005

公用表表达式 (CTE) 可以认为是在单个 SELECT.INSERT.UPDATE.DELETE 或 CREATE VIEW 语句的执行范围内定义的临时结果集. CTE 与派生表类似,具体表现在不存储为对象,并且只在查询期间有效. 与派生表的不同之处在于,CTE 可自引用,还可在同一查询中引用多次. CTE可用于: 1.创建递归查询(我个人认为CTE最好用的地方) 2.在同一语句中多次引用生成的表 CTE优点: 使用 CTE 可以获得提高可读性和轻松维护复杂查询的优点. 查询可以分为单独块.简

关于SQL中CTE(公用表表达式)(Common Table Expression)的总结_MsSql

一.WITH AS的含义 WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到.有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分. 特别对于UNION ALL比较有用.因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可.如果WITH AS短语所定义的表名

关于SQL中CTE(公用表表达式)(Common Table Expression)的总结

一.WITH AS的含义 WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到.有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分. 特别对于UNION ALL比较有用.因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可.如果WITH AS短语所定义的表名

关于使用CTE(公用表表达式)的递归查询

本文转载:http://www.cnblogs.com/shuangnet/archive/2013/03/22/2975929.html   公用表表达式 (CTE) 具有一个重要的优点,那就是能够引用其自身,从而创建递归 CTE.递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式. 当某个查询引用递归 CTE 时,它即被称为递归查询.递归查询通常用于返回分层数据,例如:显示某个组织图中的雇员或物料清单方案(其中父级产品有一个或多个组件,而那些组件可能还有

SQL Server2005杂谈(1):使用公用表表达式(CTE)简化嵌套SQL

本文为原创,如需转载,请注明作者和出处,谢谢!    先看下面一个嵌套的查询语句: select * from person.StateProvince where CountryRegionCode in           (select CountryRegionCode from person.CountryRegion where Name like 'C%')     上面的查询语句使用了一个子查询.虽然这条SQL语句并不复杂,但如果嵌套的层次过多,会使SQL语句非常难以阅读和维护.

SQL Server2005杂谈(3):公用表表达式(CTE)的递归调用

 本文为原创,如需转载,请注明作者和出处,谢谢! 先看如下一个数据表(t_tree):     上图显示了一个表中的数据,这个表有三个字段:id.node_name.parent_id.实际上,这个表中保存了一个树型结构,分三层:省.市.区.其中id表示当前省.市或区的id号.node_name表示名称.parent_id表示节点的父节点的id.     现在有一个需求,要查询出某个省下面的所有市和区(查询结果包含省).如果只使用SQL语句来实现,需要使用到游标.临时表等技术.但在SQL Ser

Sql — CTE公用表表达式和With用法总结

CTE(Common Table Expression) 公用表表达式,它是在单个语句的执行范围内定义的临时结果集,只在查询期间有效.它可以自引用,也可在同一查询中多次引用,实现了代码段的重复利用. CTE最大的好处是提升T-Sql代码的可读性,可以更加优雅简洁的方式实现递归等复杂的查询. CTE可用于: ⒈ 创建递归查询,这个应该是CTE最好用的地方 ⒉ 在同一语句中多次引用生成的表 3. 减少子查询和表变量,提高执行效率 CTE优点: 1. 使用 CTE 可以获得提高可读性和轻松维护复杂查询

SqlServer使用公用表表达式(CTE)实现无限级树形构建

SQL Server 2005开始,我们可以直接通过CTE来支持递归查询,CTE即公用表表达式 公用表表达式(CTE),是一个在查询中定义的临时命名结果集将在from子句中使用它.每个CTE仅被定义一次(但在其作用域内可以被引用任意次),并且在该查询生存期间将一直生存.可以使用CTE来执行递归操作. DECLARE @Level INT=3 ;WITH cte_parent(CategoryID,CategoryName,ParentCategoryID,Level) AS ( SELECT c