在编写T-SQL代码时,每每须要临时存储某些结果集。前面咱们已经普遍使用和介绍了两种临时存储结果集的方法:临时表和表变量。除此以外,还能够使用公用表表达式的方法。公用表表达式(Common Table Expression)是SQL Server2005版本的引入的一个特性。CTE能够看组是一个临时的结果集,能够再接下来来的一个SELECT,INSERT,UPDATE,DELETE,MERGE语句中屡次引用。使用公用表达式CTE可让语句更加清晰简练。与公用表达式做用相似的还有临时表和表变量。下面给出三种方法的对比。数据库
一、3种方法比较express
(一)、临时表:须要在临时数据库TempDB中经过I/O操做来建立表结构,一旦用户推出SQL Server环境则自动被删除。优化
(二)、表变量:在内存中以表结构的形式存在,其定义与变量一致,其使用与表相似,不须要产生I/O。spa
(三)、公用表表达式:Common Table Expression,定义在内存中保存的临时存储结果集对象,不产生I/O,不须要按照表变量这样定义,使用方法和表相似。能够本身引用,也能够再查询中被屡次引用。3d
二、WITH AS的含义code
WITH AS-作子查询部分(subquery factoring)。对象
它用于定义一个SQL片断,该片断会被是整个SQL语句所用到。若是WITH AS因此定的表名被调用两次以上,则优化器会自动将WITH AS所获取的数据放入临时表里,若是只是被调用一次,则不会。能够经过materialize将WITH AS短语里的数据强制放入全局临时表里。blog
WITH AS能够被紧跟着的一条SQL语句所使用屡次,但不能被紧跟着的多条SQL语句使用。递归
WITH B AS ( SELECT * FROM xxx WHERE Id > 5 ) SELECT * FROM B
三、CTE的定义内存
CTE的定义语法以下,主要包括3个部分。
(一)、Expression_name:CTE表达式的名称。
(二)、Column_name:列名列表。
(三)、CTE_query_definition:定义CTE结果集的Select查询语句
WITH expression_name [(column_name [,...n] )] AS (
cte_query_definition
)
根据微软对CTE好处的描述,能够归结为四点:
按照是否递归,能够将公用表(CTE)表达式分为递归公用表表达式和非递归公用表表达式.
非递归公用表表达式(CTE):
非递归公用表表达式(CTE)是查询结果仅仅一次性返回一个结果集用于外部查询调用。并不在其定义的语句中调用其自身的CTE。
非递归公用表表达式(CTE)的使用方式和视图以及子查询一致。
好比一个简单的非递归公用表表达式:
WITH CTE_Test AS ( SELECT * FROM Person_1 ) SELECT * FROM CTE_Test
公用表表达式的好处之一是能够在接下来一条语句中屡次引用:
WITH CTE_Test AS ( SELECT * FROM Person_1 ) SELECT * FROM CTE_Test AS a --第一次引用 INNER JOIN CTE_Test AS b --第二次引用 ON a.Id = b.Id ORDER BY a.Id DESC
虽然以上引用了屡次,可是只是一条语句,因此能够正常执行。
若是多条语句引用,以下面这样,是会报错的。
WITH CTE_Test AS ( SELECT * FROM Person_1 ) SELECT * FROM CTE_Test SELECT * FROM CTE_Test
输出结果以下:
因为CTE只能在接下来一条语句中使用,所以,当须要接下来的一条语句中引用多个CTE时,能够定义多个,中间用逗号分隔,下面是一次定义多个CTE的例子:
WITH CTE_Test1 AS ( SELECT * FROM Person_1 ), CTE_Test2 AS ( SELECT * FROM Person_2 ) SELECT * FROM CTE_Test1 UNION SELECT * FROM CTE_Test2
结果以下:
递归公用表表达式(CTE):
对于递归公用表达式来讲,只须要在语句中定义两部分:
先建一张表栏目表以下,栏目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 * FRON COL_CTE。这正是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 OPTION(MAXRECURSION 2) --指定最大递归次数为2
咱们知道在上面的查询中,要查到天河区新闻最少要递归3次,可是如今只递归2次,运行是什么结果呢?
提示信息以下:
消息 530,级别 16,状态 1,第 1 行
语句被终止。完成执行语句前已用完最大递归 2。
CTE是一种十分优雅的存在。CTE所带来最大的好处是代码可读性的提高,这是良好代码的必须品质之一。使用递归CTE能够更加轻松愉快的用优雅简洁的方式实现复杂的查询。