本文转自:爽朗的微笑  http://www.cnblogs.com/shuangnet/archive/2013/03/22/2975929.html

  公用表表达式 (CTE) 具有一个重要的优点,那就是能够引用其自身,从而创建递归 CTE。递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式。

  当某个查询引用递归 CTE 时,它即被称为递归查询。递归查询通常用于返回分层数据,例如:显示某个组织图中的雇员或物料清单方案(其中父级产品有一个或多个组件,而那些组件可能还有子组件,或者是其他父级产品的组件)中的数据。

  递归 CTE 可以极大地简化在 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 语句中运行递归查询所需的代码。在 SQL Server 的早期版本中,递归查询通常需要使用临时表、游标和逻辑来控制递归步骤流。 

CTE 的基本语法结构如下:

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

    AS

    ( CTE_query_definition )

    --只有在查询定义中为所有结果列都提供了不同的名称时,列名称列表才是可选的。

    --运行 CTE 的语句为:

    SELECT <column_list> FROM expression_name;

在使用CTE时应注意如下几点:

CTE后面必须直接跟使用CTE的SQL语句(如select、insert、update等),否则,CTE将失效。如下面的SQL语句将无法正常使用CTE:

  with
  cr as
  (
  select * from 表名 where 条件
   )
  --select * from person.CountryRegion --如果加上这句话后面用到cr将报错
  select * from cr

2. CTE后面也可以跟其他的CTE,但只能使用一个with,多个CTE中间用逗号(,)分隔,如下面的SQL语句所示:

with
cte1 as
(
select * from table1 where name like '测试%'
),
cte2 as
(
select * from table2 where id > 20
),
cte3 as
(
select * from table3 where price < 100
)
select a.* from cte1 a, cte2 b, cte3 c where a.id = b.id and a.id = c.id

3. 如果CTE的表达式名称与某个数据表或视图重名,则紧跟在该CTE后面的SQL语句使用的仍然是CTE,当然,后面的SQL语句使用的就是数据表或视图。

4. CTE 可以引用自身,也可以引用在同一 WITH 子句中预先定义的 CTE。

5. 不能在 CTE_query_definition 中使用以下子句:

  •  COMPUTE 或 COMPUTE BY
  •  ORDER BY(除非指定了 TOP 子句)
  •  INTO
  •  带有查询提示的 OPTION 子句
  •  FOR XML
  •  FOR BROWSE

6. 如果将 CTE 用在属于批处理的一部分的语句中,那么在它之前的语句必须以分号结尾,如下面的SQL所示:

declare @s nvarchar(3)
set @s = '测试%'; -- 必须加分号
with
t_tree as
(
select * from 表 where 字段 like @s
)
select * from t_tree

------------------------------------操作------------------------------------

上面可能对with as说的有点儿啰嗦了,下面进入正题:

老规矩先建表(Co_ItemNameSet):

CREATE TABLE [dbo].[Co_ItemNameSet](
[ItemId] [int] NULL,
[ParentItemId] [int] NULL,
[ItemName] [nchar](10) COLLATE Chinese_PRC_CI_AS NULL
) ON [PRIMARY]

插入数据:

--给表插入数据
insert into dbo.Co_ItemNameSet values(2,0,'管理费用')
insert into dbo.Co_ItemNameSet values(3,0,'销售费用')
insert into dbo.Co_ItemNameSet values(4,0,'财务费用')
insert into dbo.Co_ItemNameSet values(5,0,'生产成本')
insert into dbo.Co_ItemNameSet values(35,5,'材料')
insert into dbo.Co_ItemNameSet values(36,5,'人工')
insert into dbo.Co_ItemNameSet values(37,5,'制造费用')
insert into dbo.Co_ItemNameSet values(38,35,'原材料')
insert into dbo.Co_ItemNameSet values(39,35,'主要材料')
insert into dbo.Co_ItemNameSet values(40,35,'间辅材料')
insert into dbo.Co_ItemNameSet values(41,36,'工资')
insert into dbo.Co_ItemNameSet values(42,36,'福利')
insert into dbo.Co_ItemNameSet values(43,2,'管理费用子项')
insert into dbo.Co_ItemNameSet values(113,43,'管理费用子项的子项')

查询插入的数据:

--查询数据
select * from Co_ItemNameSet

结果图:

题目需求是:查询ItemId=2及子节点,也就是管理费用和其下属所有节点的信息

操作1:先看看不用CTE递归操作的sql语句如下(需要真是的建两个表进行数据的存放和判断,非常麻烦):

 declare @i int
select @i=2; create table #tem(
[ItemId] [INT] NOT NULL,
[level] INT
); create table #list(
[ItemId] [INT] NOT NULL,
[ParentItemId] [INT] NOT NULL default ((0)),
[ItemName] [nvarchar](100) NOT NULL default (''),
[level] int
); insert INTO #tem([ItemId],[level])
select ItemId,1
from Co_ItemNameSet
where itemid=@i insert into #list([ItemId],[ParentItemId],[ItemName],[level])
select ItemId,ParentItemId,ItemName,1
from Co_ItemNameSet
where itemid=@i declare @level int
select @level=1
declare @current INT
select @current=0 while(@level>0)
begin
select @current=ItemId
from #tem
where [level]=@level
if @@ROWCOUNT>0
begin delete from #tem
where [level]=@level and ItemId=@current insert into #tem([ItemId],[level])
select [ItemId],@level+1
from Co_ItemNameSet
where ParentItemId=@current insert into #list([ItemId],[ParentItemId],[ItemName],[level])
select [ItemId],[ParentItemId],[ItemName],@level+1
from Co_ItemNameSet
where ParentItemId=@current
if @@rowcount>0
begin
select @level=@level+1
end
end
else
begin
select @level=@level-1
end
end select * from #list
drop table #tem
drop table #list

结果图:

操作2:用CTE递归操作的sql语句如下:

DECLARE @i INT
SELECT @i=2;
WITH Co_ItemNameSet_CTE(ItemId,ParentItemId,ItemName,[Level])
AS
(
SELECT ItemId,ParentItemId,ItemName,1 AS [Level]
FROM Co_ItemNameSet
WHERE itemid=@i
UNION ALL
SELECT c.ItemId,c.ParentItemId,c.ItemName,[Level] + 1
FROM Co_ItemNameSet c INNER JOIN Co_ItemNameSet_CTE ct
ON c.ParentItemId=ct.ItemId
)
SELECT * FROM Co_ItemNameSet_CTE

结果图:

-----------------------------分析(查看MSDN的分析)----------------------------

主要分析一下用CTE的递归操作:

递归 CTE 由下列三个元素组成:

  1. 例程的调用。

    递归 CTE 的第一个调用包括一个或多个由 UNION ALL、UNION、EXCEPT 或 INTERSECT 运算符联接的 CTE_query_definitions。由于这些查询定义形成了 CTE 结构的基准结果集,所以它们被称为“定位点成员”。

    CTE_query_definitions 被视为定位点成员,除非它们引用了 CTE 本身。所有定位点成员查询定义必须放置在第一个递归成员定义之前,而且必须使用 UNION ALL 运算符联接最后一个定位点成员和第一个递归成员。

  2. 例程的递归调用。

    递归调用包括一个或多个由引用 CTE 本身的 UNION ALL 运算符联接的 CTE_query_definitions(就是as里的语句块)。这些查询定义被称为“递归成员”。

  3. 终止检查。

    终止检查是隐式的;当上一个调用中未返回行时,递归将停止。

递归 CTE 结构必须至少包含一个定位点成员和一个递归成员。以下伪代码显示了包含一个定位点成员和一个递归成员的简单递归 CTE 的组件。

WITH cte_name ( column_name [,...n] )
AS

CTE_query_definition --定位点成员
UNION ALL
CTE_query_definition --递归成员.

现在让我们看一下递归执行过程:

  1. 将 CTE 表达式拆分为定位点成员和递归成员。

  2. 运行定位点成员,创建第一个调用或基准结果集 (T0)。

  3. 运行递归成员,将 Ti 作为输入,将 Ti+1 作为输出。

  4. 重复步骤 3,直到返回空集。

  5. 返回结果集。这是对 T0 到 Tn 执行 UNION ALL 的结果。

参考文档:

WITH common_table_expression (Transact-SQL)

https://msdn.microsoft.com/en-us/library/ms175972.aspx

【转】CTE(公用表表达式)的更多相关文章

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

    --关于使用CTE(公用表表达式)的递归查询 --CTE 的基本语法结构如下: WITH expression_name [ ( column_name [,...n] ) ] AS ( CTE_qu ...

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

    CTE(Common Table Expression) 公用表表达式,它是在单个语句的执行范围内定义的临时结果集,只在查询期间有效.它可以自引用,也可在同一查询中多次引用,实现了代码段的重复利用. ...

  3. sql中with的用法(CTE公用表表达式):应用子查询嵌套,提高sql性能

    一.WITH AS的含义 WITH AS短语,也叫子查询部分(subquery factoring),定义一个SQL片断,该片断会被整个SQL语句所用到. 有时是为了让SQL语句的可读性更高些,也可能 ...

  4. 使用CTE公用表表达式的递归查询(WITH AS)

    公用表表达式 (CTE) 具有一个重要的优点,那就是能够引用其自身,从而创建递归 CTE.递归 CTE 是一个重复执行初始 CTE 以返回数据子集直到获取完整结果集的公用表表达式. 当某个查询引用递归 ...

  5. CTE(公用表表达式)

    -> 将复杂的派生表写在中间from子句中变得十分臃肿,给为维护等操作带来麻烦 -> 将这个派生表要是能提前到前面,给一个别名,后面查询的时候直接使用别名即可语法: with 表的别名 a ...

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

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

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

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

  8. 存储过程——公用表表达式(CTE)

    目录 0. 背景说明 1. 定义及语法细节 1.1 基本定义 1.2 基本语法 1.3 多个CTE同时声明 1.4 CTE嵌套使用 2. CTE递归查询 2.1 简介 2.2 准备工作 2.3 计算每 ...

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

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

随机推荐

  1. codevs 1183 泥泞的道路 01分数规划

    题目链接 题目描述 Description CS有n个小区,并且任意小区之间都有两条单向道路(a到b,b到a)相连.因为最近下了很多暴雨,很多道路都被淹了,不同的道路泥泞程度不同.小A经过对近期天气和 ...

  2. android 源代码快速搜索引擎OpenCrok

    使用OpenGrok的Android系统源码搜索引擎,搜索速度简直太快速了!: http://androidxref.com/ 另外,OpenGrok的也是可以在本地计算机中安装配置的,主要是安装To ...

  3. IE 中开发,兼容与性能测试工具汇总

    前言 对于开发者来说, IE的兼容性是最让人头疼的. 因为是微软的产品, 且绑定在操作系统上, 所以IE的占用率还是相当大, 对于开发者来说, 这部分的兼容的考虑就不可避免了. 对于IE 的各版本来说 ...

  4. 第八届河南省赛D.引水工程(kruthcra+prime)

    D.引水工程 Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 118  Solved: 41 [Submit][Status][Web Board] D ...

  5. Android 自己主动化測试之------ Monkey工具

    尽管 一般公司都有专门的測试人员,可是有时候 免不了 我们既要去开发产品,也要去測试产品,測试产品.有些机械化的 点界面的操作,谷歌已经给我们提供了工具.Monkey, 猴子測试. 什么是Monkey ...

  6. 假设将synthesize省略,语义特性声明为assign retain copy时,自己实现setter和getter方法

    假设将synthesize省略,而且我们自己实现setter和getter方法时,系统就不会生成相应的setter和getter方法,还有实例变量 1,当把语义特性声明为assign时,setter和 ...

  7. Java 程序中的多线程

    概述 synchronized  关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C. D等)正在用这个方法,有的话要等正在使用synch ...

  8. xcode Simulated Metrics xib设置小问题

  9. java内存映射文件

    内存映射文件能够让我们创建和修改大文件(大到内存无法读入得文件),对于内存映射文件,我们可以认为是文件已经全部被读入到内存当中,然后当成一个大的数字来访问,简化修改文件的代码. 1.directBuf ...

  10. 条款16:成对使用new和delete时,采取相同的形式

    问题聚焦:     我们都知道,new和delete要成对使用,但是有时候,事情往往不是按我们预期的那样发展.     对于单一对象和对象数组,我们要分开考虑.     遇到typedef时,也需要搞 ...