在编写T-SQL代码时,往往需要临时存储某些结果集。在CTE之前常用的两种临时存储结果集为:临时表和表变量。除此之外,还可以使用公用表表达式的方法。

  公用表表达式(Common Table Expression,CTE)是SQL Server2005版本的引入的一个特性。CTE可以看作是一个临时的结果集,可以再接下来来的一个SELECT,INSERT,UPDATE,DELETE,MERGE 语句中多次引用。使用公用表达式CTE可以让语句更加清晰简练。

一、三种方式的对比

  1、临时表:需要在临时数据库TempDB中通过I/O操作来创建表结构,一旦用户退出 SQL Server 环境则自动被删除。

  2、表变量:在内存中以表结构的形式存在,其定义与变量一致,其使用与表类似,不需要产生I/O。

  3、CTE:定义在内存中保存的临时存储结果集对象,不产生I/O,不需要按照表变量这样定义,使用方法和表类似。可以自己引用,也可以在查询中被多次引用。

二、关键字“ WITH AS ”

  WITH AS-做子查询部分(subquery factoring)。

  它用于定义一个SQL片段,该片段会被是整个SQL语句所用到。如果WITH AS所定义的表名被调用两次以上,则优化器会自动将WITH AS所获取的数据放入临时表里,如果只是被调用一次,则不会。可以通过 materialize 将WITH AS短语里的数据强制放入全局临时表里。

  WITH AS可以被紧跟着的一条SQL语句所使用多次,但不能被紧跟着的多条SQL语句使用。

WITH B AS
(
SELECT * FROM xxx WHERE Id > 5
)
SELECT * FROM B

三、CTE的定义

  CTE的定义语法如下,主要包括3个部分。

  1、Expression_name:CTE表达式的名称。

  2、Column_name:列名列表。

  3、CTE_query_definition:定义CTE结果集的 SEECT 查询语句

WITH expression_name [(column_name [,...n] )]
AS
(
CTE_query_definition
)

  根据微软对CTE好处的描述,可以归结为四点:

  [1] 可以定义递归公用表表达式(CTE);

  [2] 当不需要将结果集作为视图被多个地方引用时,CTE可以使其更加简洁;

  [3] GROUP BY语句可以直接作用于子查询所得的标量列;

  [4] 可以在接下来的一个 SELECT 语句中多次引用公用表表达式(CTE);

  按照是否递归,可以将公用表(CTE)表达式分为递归公用表表达式和非递归公用表表达式.

三、非递归公用表表达式(CTE):

  非递归公用表表达式(CTE)是查询结果仅仅一次性返回一个结果集用于外部查询调用。并不在其定义的语句中调用其自身的CTE。

  非递归公用表表达式(CTE)的使用方式和视图以及子查询一致。

  比如一个简单的非递归公用表表达式:

WITH CTE_Test
AS
(
SELECT * FROM User
)
SELECT * FROM CTE_Test;

  CTE 可以在接下来一条语句中多次引用:

    WITH CTE_Test
  AS
  (
  SELECT * FROM User
  )
  SELECT * FROM CTE_Test AS a  --第一次引用
  INNER JOIN CTE_Test AS b    --第二次引用
  ON a.Id = b.Id
  ORDER BY a.Id DESC

  以上引用了多次,但是都在一条 SELECT 语句中,可以正常执行。

  如果多条语句引用,如下面这样,是会报错的。

    WITH CTE_Test
  AS
  (
  SELECT * FROM User
  )
  SELECT * FROM CTE_Test;   SELECT * FROM CTE_Test; //对象“CTE_Test”不存在

  由于CTE只能在接下来一条语句中使用,因此,当需要接下来的一条语句中引用多个CTE时,可以定义多个,中间用逗号分隔,下面是一次定义多个CTE的例子:

WITH CTE_Test1
AS
(
SELECT * FROM table1
),
CTE_Test2
AS
(
SELECT * FROM table2
)
SELECT * FROM CTE_Test1
UNION
SELECT * FROM CTE_Test2

四、递归公用表表达式(CTE):

  递归公用表表达式很像派生表(Derived Tables ),指的是在CTE内的语句中调用其自身的CTE。与派生表不同的是,CTE可以在一次定义多次进行派生递归。对于递归的概念,是指一个函数或是过程直接或者间接的调用其自身。要构成递归函数,需要两部分:第一部分是基础部分,返回固定值,也就是告诉程序何时开始递归;第二部分是循环部分,是函数或过程直接或者间接调用自身进行递归。

  对于递归公用表达式来说,实现原理也是相同的,同样需要在语句中定义两部分:

  [1] 基本语句;

  [2] 递归语句;

  先建一张表栏目表如下,栏目Id,栏目名称,栏目的父栏目。

  

  现在使用CTE查询其每个栏目是第几层栏目的代码如下:

WITH COL_CTE(Id,Name,ParentId,tLevel )
AS
(
--基本语句
SELECT Id,Name,ParentId,0 AS tLevel FROM Col
WHERE ParentId = 0
UNION ALL
--递归语句
SELECT c.Id,c.Name,c.ParentId,ce.tLevel+1 AS tLevel FROM COL as c
INNER JOIN COL_CTE AS ce   --递归调用
ON c.ParentId = ce.Id
) SELECT * FROM COL_CTE

  输出结果如下:

  

  0表示顶级栏目。1就是1级栏目。语法非常优雅。就一个SELECT * FROM COL_CTE。这正是CTE强大的地方,当然,越强大的力量,就需要被约束。如果使用不当的话,递归CTE可能会出现无限递归。从而大量消耗SQL Server的服务器资源。因此,SQL Server提供了OPTION选项( OPTION(MAXRECURSION 2) ),可以设定最大的递归次数。

  如将上面的查询语法改为:

WITH COL_CTE(Id,Name,ParentId,tLevel )
AS
(
--基本语句
SELECT Id,Name,ParentId,0 AS tLevel FROM Col
WHERE ParentId = 0
UNION ALL
--递归语句
SELECT c.Id,c.Name,c.ParentId,ce.tLevel+1 AS tLevel FROM COL as c
INNER JOIN COL_CTE AS ce
ON c.ParentId = ce.Id
) SELECT * FROM COL_CTE
OPTION(MAXRECURSION 2)  --指定最大递归次数为2

 我们知道在上面的查询中,要查到天河区新闻最少要递归3次,但是现在只递归2次,运行是什么结果呢?

  

  提示信息如下:

  消息 530,级别 16,状态 1,第 1 行
  语句被终止。完成执行语句前已用完最大递归 2。

  CTE是一种十分优雅的存在。CTE所带来最大的好处是代码可读性的提升,这是良好代码的必须品质之一。使用递归CTE可以更加轻松愉快的用优雅简洁的方式实现复杂的查询。

T-SQL 之 公用表表达式(CTE)的更多相关文章

  1. SQL Server 公用表表达式(CTE)实现递归

    公用表表达式简介: 公用表表达式 (CTE) 可以认为是在单个 SELECT.INSERT.UPDATE.DELETE 或 CREATE VIEW 语句的执行范围内定义的临时结果集.CTE 与派生表类 ...

  2. SQL Server 公用表表达式(CTE)实现递归的方法

    公用表表达式简介: 公用表表达式 (CTE) 可以认为是在单个 SELECT.INSERT.UPDATE.DELETE 或 CREATE VIEW 语句的执行范围内定义的临时结果集.CTE 与派生表类 ...

  3. SQL Server进阶(六)表表达式--派生表、公用表表达式(CTE)、视图和内联表值函数

    概述 表表达式是一种命名的查询表达式,代表一个有效地关系表.可以像其他表一样,在数据处理中使用表表达式. SQL Server支持四种类型的表表达式:派生表,公用表表达式,视图和内联表值函数. 为什么 ...

  4. SQL Server温故系列(3):SQL 子查询 & 公用表表达式 CTE

    1.子查询 Subqueries 1.1.单行子查询 1.2.多行子查询 1.3.相关子查询 1.4.嵌套子查询 1.5.子查询小结及性能问题 2.公用表表达式 CTE 2.1.普通公用表表达式 2. ...

  5. SQL Server 表表达式--派生表、公用表表达式(CTE)、视图和内联表值函数

    概述 表表达式是一种命名的查询表达式,代表一个有效地关系表.可以像其他表一样,在数据处理中使用表表达式. SQL Server支持四种类型的表表达式:派生表,公用表表达式,视图和内联表值函数. 为什么 ...

  6. SQL Server中公用表表达式 CTE 递归的生成帮助数据,以及递归的典型应用

    本文出处:http://www.cnblogs.com/wy123/p/5960825.html 我们在做开发的时候,有时候会需要一些帮助数据,必须需要连续的数字,连续间隔的时间点,连续的季度日期等等 ...

  7. sql server使用公用表表达式CTE通过递归方式编写通用函数自动生成连续数字和日期

    问题:在数据库脚本开发中,有时需要生成一堆连续数字或者日期,例如yearly report就需要连续数字做年份,例如daily report就需要生成一定时间范围内的每一天日期.而自带的系统表mast ...

  8. T-SQL 公用表表达式(CTE)

    公用表表达式(CTE) 在编写T-SQL代码时,往往需要临时存储某些结果集.前面我们已经广泛使用和介绍了两种临时存储结果集的方法:临时表和表变量.除此之外,还可以使用公用表表达式的方法.公用表表达式( ...

  9. 详解公用表表达式(CTE)

    简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的.但是,视图是作为系统对象存在数据库中,那对于结果集 ...

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

    简介 对于SELECT查询语句来说,通常情况下,为了使T-SQL代码更加简洁和可读,在一个查询中引用另外的结果集都是通过视图而不是子查询来进行分解的. 但是,视图是作为系统对象存在数据库中,那对于结果 ...

随机推荐

  1. cloudstack ssvm 管理地址不够造成无法启动修复过程

    cloudstack日志记录: 上面已经提示了,管理ip没有了,造成这个原因很多,遇到过ssvm非正常关闭就有可能不释放IP慢慢把IP消耗掉.总之这肯定是BUG.按照上面的提示找到对应pod 和dc ...

  2. Scrapy实战篇(八)之简书用户信息全站抓取

    相对于知乎而言,简书的用户信息并没有那么详细,知乎提供了包括学习,工作等在内的一系列用户信息接口,但是简书就没有那么慷慨了.但是即便如此,我们也试图抓取一些基本信息,进行简单地细分析,至少可以看一下, ...

  3. Redis学习篇(十二)之管道技术

    通过管道技术降低往返时延 当后一条命令不依赖于前一条命令的返回结果时,可以使用管道技术将多条命令一起 发送给redis服务器,服务器执行结束之后,一起返回结果,降低了通信频度.

  4. 常用的JVM配置参数

    一.Trace 跟踪参数 在Eclipse中,如何打开GC的监控日志 选择菜单栏Run -> Run Configurations -> Java Application -> 选择 ...

  5. 【BZOJ 2822】2822: [AHOI2012]树屋阶梯(卡特兰数+高精度)

    2822: [AHOI2012]树屋阶梯 Description 暑假期间,小龙报名了一个模拟野外生存作战训练班来锻炼体魄,训练的第一个晚上,教官就给他们出了个难题.由于地上露营湿气重,必须选择在高处 ...

  6. BZOJ 3676: [Apio2014]回文串 回文树 回文自动机

    http://www.lydsy.com/JudgeOnline/problem.php?id=3676 另一种更简单更快常数更小的写法,很神奇……背板子. #include<iostream& ...

  7. bzoj 3165

    题意: 给出平面上一些线段,在线询问与x=x0相交的线段中,交点y最大的线段的标号,支持添加线段. 大概思路: 用线段树维护,线段树每个线段记录贯穿(左右端点在该区间外或上)的原线段中能覆盖其它贯穿该 ...

  8. Codeforces Beta Round #92 (Div. 1 Only) A. Prime Permutation 暴力

    A. Prime Permutation 题目连接: http://www.codeforces.com/contest/123/problem/A Description You are given ...

  9. Codeforces Round #202 (Div. 1) A. Mafia 贪心

    A. Mafia Time Limit: 20 Sec  Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/348/problem/A D ...

  10. Swift 自定义打印方法

    Swift 自定义打印方法 代码如下 // MARK:- 自定义打印方法 func MLLog<T>(_ message : T, file : String = #file, funcN ...