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

2016-02-19 15:20 19 1 收藏

每个人都希望每天都是开心的,不要因为一些琐事扰乱了心情还,闲暇的时间怎么打发,关注图老师可以让你学习更多的好东西,下面为大家推荐SQL Server2005杂谈(2):公用表表达式(CTE)的递归调用,赶紧看过来吧!

【 tulaoshi.com - 编程语言 】

  先看如下一个数据表(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 allcurrent_resultset

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

publicresultsetgetResultSet(resultset)
{
  if(resultsetisnull)
  {
    current_resultset=第一个结果集(包含省的记录集)
    将结果集的id保存在集合中
    getResultSet(current_resultset)
  }
  current_resultset=根据id集合中的id值查出当前结果集
  if(current_resultisnull)returnresultset
  将当前结果集的id保存在集合中
  return getResultSet(resultsetunionallcurrent_resultset)
}
//获得最终结果集
resultset=getResultSet(null)

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

  实现递归的CTE语法如下:

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

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

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

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

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

with
districtas
(
  select*fromt_treewherenode_name=N'辽宁省'
  unionall
  selecta.*fromt_treea,districtb
       wherea.parent_id=b.id
),
district1as
(
  selecta.*fromdistrictawherea.idin(selectparent_idfromdistrict)  
)
select*fromdistrict1

  查询结果如下图所示。

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

  在定义和使用递归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)子查询

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

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

  7.无论参与的 SELECT 语句返回的列的为空性如何,递归 CTE 返回的全部列都可以为空。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

  8.如果递归 CTE 组合不正确,可能会导致无限循环。例如,如果递归成员查询定义对父列和子列返回相同的值,则会造成无限循环。可以使用 MAXRECURSION 提示以及在 INSERT、UPDATE、DELETE 或 SELECT 语句的 OPTION 子句中的一个 0 到 32,767 之间的值,来限制特定语句所允许的递归级数,以防止出现无限循环。这样就能够在解决产生循环的代码问题之前控制语句的执行。服务器范围内的默认值是 100。如果指定 0,则没有限制。每一个语句只能指定一个 MAXRECURSION 值。

  9.不能使用包含递归公用表表达式的视图来更新数据。

  10.可以使用 CTE 在查询上定义游标。递归 CTE 只允许使用快速只进游标和静态(快照)游标。如果在递归 CTE 中指定了其他游标类型,则该类型将转换为静态游标类型。

  11.可以在 CTE 中引用远程服务器中的表。如果在 CTE 的递归成员中引用了远程服务器,那么将为每个远程表创建一个假脱机,这样就可以在本地反复访问这些表。

来源:http://www.tulaoshi.com/n/20160219/1609440.html

延伸阅读
标签: SQLServer
一、 CLR安全性 在 第一篇 中,我们已经讨论了宿主于和在SQL Server内执行的.NET代码的安全环境-从SQL Server的角度来观察SQLCLR代码模块。但是CLR使用其自己的安全模型。一旦SQL Server同意进行所有的许可权检查并且允许代码执行,那么这种模型就会"强制介入"。仅仅因为它能够执行并不意味着它能够做它想做的任何事情。 ...
一、 SQLCLR权限集级别 当你使用CREATE ASSEMBLY语句把一个程序集加载到一个数据库中时,SQL Server提供了三种权限集级别:SAFE,EXTERNAL_ACCESS和UNSAFE。这些权限集形成如图3和图5(均请参考第二篇)所示的AppDomain策略级别。 下面是一个典型的语句,它实现安装位于FileLoader.dll文件内的一个程序集,并且赋予它EXTERNAL_AC...
针对与mssql2005的安全,应当针对于2个方面来做,针对于主要的权限及端口。(要是有人说删除不安全的系统存储过程,先说好这个方式只能针对于2000,2005及2008均未发现可以删除的方法,有的话请回复)。 1.端口方面大家都知道mssql默认端口为1433,对于默认端口我一般都有一种想要封掉的冲动,主要是大家只要telnet一下就知道我的数据库是什么...
标签: Web开发
在对正则表达式有了较为全面的了解之后,我们就来看一下如何在Perl,PHP,以及JavaScript中使用正则表达式。 通常,Perl中正则表达式的使用格式如下: operator / regular-expression / string-to-replace / modifiers 运算符一项可以是m或s,分别代表匹配运算和替换运算。 其中,正则表达式一项是将要进行匹配或替换...
标签: Web开发
我们要解析一个HTML文档时可利用正则表达式取得标签内容 例子: 以从字符串中取出所有A标签的 id号和内容为例: a target="_blank"aaaaaaaaaa/a 正则表达式: a[^]*id[^]*=[^]*"(?ID[^]*)"[^]*target[^]*=[^]*"[^]*_blank[^]*" [^]*(?content[^]*)/a 正则分解: [^]* 是一个很有用的组合,能定位到下一个查询关键字 (?ID[^]*) 用于...

经验教程

43

收藏

92
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部