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

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: 本文为原创,如需转载,请注明作者和出处,谢谢! 上一篇:SQL Server2005杂谈(1):使用公用表表达式(CTE)简化嵌套SQL 先看如下一个数据表(t_tree):     上图显示了一个表中的数据,这个表有三个字段:id、node_name、parent_id。
本文为原创,如需转载,请注明作者和出处,谢谢!

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

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

    上图显示了一个表中的数据,这个表有三个字段:idnode_nameparent_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字段值为23的记录,如下:

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 ALLUNIONINTERSECT EXCEPT。在最后一个定位点成员和第一个递归成员之间,以及组合多个递归成员时,只能使用 UNION ALL 集合运算符。
3. 
定位点成员和递归成员中的列数必须一致。
4. 递归成员中列的数据类型必须与定位点成员中相应列的数据类型一致。
5. 
递归成员的 FROM 子句只能引用一次 CTE expression_name
6. 在递归成员的 CTE_query_definition 中不允许出现下列项:

1SELECT DISTINCT

2GROUP BY

3HAVING

4)标量聚合

5TOP

6LEFTRIGHTOUTER JOIN(允许出现 INNER JOIN

7)子查询

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

7. 无论参与的 SELECT 语句返回的列的为空性如何,递归 CTE 返回的全部列都可以为空。
8. 如果递归 CTE 组合不正确,可能会导致无限循环。例如,如果递归成员查询定义对父列和子列返回相同的值,则会造成无限循环。可以使用 MAXRECURSION 提示以及在 INSERTUPDATEDELETE 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开发速学宝典》出版,欢迎定购

相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
目录
相关文章
|
5月前
|
SQL 存储 缓存
揭秘SQL中的公用表表达式:数据查询的新宠儿
揭秘SQL中的公用表表达式:数据查询的新宠儿
63 2
|
6月前
|
消息中间件 SQL RocketMQ
RocketMQ-初体验RocketMQ(10)-过滤消息_SQL92表达式筛选消息
RocketMQ-初体验RocketMQ(10)-过滤消息_SQL92表达式筛选消息
147 0
|
6月前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL表达式:数据的魔法公式
【4月更文挑战第19天】探索Oracle PL/SQL表达式,体验数据的魔法公式。表达式结合常量、变量、运算符和函数,用于数据运算与转换。算术运算符处理数值计算,比较运算符执行数据比较,内置函数如TO_CHAR、ROUND和SUBSTR提供多样化操作。条件表达式如CASE和NULLIF实现灵活逻辑判断。广泛应用于SQL查询和PL/SQL程序,助你驾驭数据,揭示其背后的规律与秘密,成为数据魔法师。
|
6月前
|
SQL Java 关系型数据库
MyBatis的动态SQL之OGNL(Object-Graph Navigation Language)表达式以及各种标签的用法
MyBatis的动态SQL之OGNL(Object-Graph Navigation Language)表达式以及各种标签的用法
109 0
|
6月前
|
SQL
leetcode-SQL-1440. 计算布尔表达式的值
leetcode-SQL-1440. 计算布尔表达式的值
62 1
|
6月前
|
SQL 数据挖掘 数据处理
「SQL面试题库」 No_109 计算布尔表达式的值
「SQL面试题库」 No_109 计算布尔表达式的值
|
6月前
|
SQL Oracle 关系型数据库
Oracle PL/SQL 第三章--运算符与表达式
Oracle PL/SQL 第三章--运算符与表达式
|
SQL 分布式计算 大数据
分享一个 HIVE SQL 性能优化点-使用公共表表达式 CTE 替换临时表
分享一个 HIVE SQL 性能优化点-使用公共表表达式 CTE 替换临时表
|
SQL 存储 数据库
盘点现在用的SqlServer 5种分页方式和拉姆达表达式分页,进来看看吧。
盘点现在用的SqlServer 5种分页方式和拉姆达表达式分页,进来看看吧。
(转)使用公用表表达式的递归查询(SQLSERVER2005)
在 SQL Server 2005 中,当某个查询引用递归 CTE 时,它即被称为“递归查询”。递归查询通常用于返回分层数据,例如:显示某个组织图中的雇员或物料清单方案(其中父级产品有一个或多个组件,而那些组件可能还有子组件,或者是其他父级产品的组件)中的数据。
下一篇
无影云桌面