SQL Server with as使用

转载xxc1605629895 最后发布于2017-11-30 21:39:53 阅读数 20366  收藏

展开

一.WITH AS的含义

WITH AS短语,也叫做子查询部分(subquery factoring),可以让你做很多事情,定义一个SQL片断,该SQL片断会被整个SQL语句所用到。有的时候,是为了让SQL语句的可读性更高些,也有可能是在UNION ALL的不同部分,作为提供数据的部分。

特别对于UNION ALL比较有用。因为UNION ALL的每个部分可能相同,但是如果每个部分都去执行一遍的话,则成本太高,所以可以使用WITH AS短语,则只要执行一遍即可。如果WITH AS短语所定义的表名被调用两次以上,则优化器会自动将WITH AS短语所获取的数据放入一个TEMP表里,如果只是被调用一次,则不会。而提示materialize则是强制将WITH AS短语里的数据放入一个全局临时表里。很多查询通过这种方法都可以提高速度。

二.使用方法

先看下面一个嵌套的查询语句:

select * from person.StateProvince where CountryRegionCode in 
         (select CountryRegionCode from person.CountryRegion where Name like 'C%')

上面的查询语句使用了一个子查询。虽然这条SQL语句并不复杂,但如果嵌套的层次过多,会使SQL语句非常难以阅读和维护。因此,也可以使用表变量的方式来解决这个问题,SQL语句如下:

declare @t table(CountryRegionCode nvarchar(3)) 
insert into @t(CountryRegionCode) (select CountryRegionCode from person.CountryRegion where Name like 'C%')

select * from person.StateProvince where CountryRegionCode 
                     in (select * from @t)

虽然上面的SQL语句要比第一种方式更复杂,但却将子查询放在了表变量@t中,这样做将使SQL语句更容易维护,但又会带来另一个问题,就是性能的损失。由于表变量实际上使用了临时表,从而增加了额外的I/O开销,因此,表变量的方式并不太适合数据量大且频繁查询的情况。为此,在SQL Server 2005中提供了另外一种解决方案,这就是公用表表达式(CTE),使用CTE,可以使SQL语句的可维护性,同时,CTE要比表变量的效率高得多。

下面是CTE的语法:

[ WITH <common_table_expression> [ ,n ] ] 
<common_table_expression>::= 
        expression_name [ ( column_name [ ,n ] ) ] 
    AS 
        ( CTE_query_definition )

现在使用CTE来解决上面的问题,SQL语句如下:

  1. with

  2. cr as

  3. (

  4. select CountryRegionCode from person.CountryRegion where Name like 'C%'

  5. )

  6. select * from person.StateProvince where CountryRegionCode in (select * from cr)

其中cr是一个公用表表达式,该表达式在使用上与表变量类似,只是SQL Server 2005在处理公用表表达式的方式上有所不同。

在使用CTE时应注意如下几点: 
1. CTE后面必须直接跟使用CTE的SQL语句(如select、insert、update等),否则,CTE将失效。如下面的SQL语句将无法正常使用CTE:

  1. with

  2. cr as

  3. (

  4. select CountryRegionCode from person.CountryRegion where Name like 'C%'

  5. )

  6. select * from person.CountryRegion -- 应将这条SQL语句去掉

  7. -- 使用CTE的SQL语句应紧跟在相关的CTE后面 --

  8. select * from person.StateProvince where CountryRegionCode in (select * from cr)

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

  1. with

  2. cte1 as

  3. (

  4. select * from table1 where name like 'abc%'

  5. ),

  6. cte2 as

  7. (

  8. select * from table2 where id > 20

  9. ),

  10. cte3 as

  11. (

  12. select * from table3 where price < 100

  13. )

  14. 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语句使用的就是数据表或视图了,如下面的SQL语句所示:

  1. -- table1是一个实际存在的表

  2. with

  3. table1 as

  4. (

  5. select * from persons where age < 30

  6. )

  7. select * from table1 -- 使用了名为table1的公共表表达式

  8. select * from table1 -- 使用了名为table1的数据表

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

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

(1)COMPUTE 或 COMPUTE BY

(2)ORDER BY(除非指定了 TOP 子句)

(3)INTO

(4)带有查询提示的 OPTION 子句

(5)FOR XML

(6)FOR BROWSE

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

  1. declare @s nvarchar(3)

  2. set @s = 'C%'

  3. ; -- 必须加分号

  4. with

  5. t_tree as

  6. (

  7. select CountryRegionCode from person.CountryRegion where Name like @s

  8. )

  9. select * from person.StateProvince where CountryRegionCode in (select * from t_tree)

CTE除了可以简化嵌套SQL语句外,还可以进行递归调用,关于这一部分的内容将在下一篇文章中介绍。

先看如下一个数据表(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值,然后就进入了的递归过程,如下图所示。

上述示例准备sql 如下:

  1. if exists (

  2. select *

  3. from sys.tables

  4. where name = 't_tree'

  5. )

  6. drop table dbo.t_tree;

  7. go

  8. create table t_tree

  9. (

  10. id int not null

  11. ,node_name varchar(50) null

  12. ,parent_id int not null

  13. ,[description] varchar(255) null

  14. )

  15. go

  16. insert into t_tree

  17. (

  18. id

  19. ,node_name

  20. ,parent_id

  21. )

  22. values

  23. (

  24. 1

  25. ,'辽宁省'

  26. ,0

  27. ),

  28. (2 ,'沈阳市' ,1),

  29. (3 ,'大连市' ,1),

  30. (4 ,'大东区' ,2),

  31. (5 ,'沈河区' ,2),

  32. (6 ,'铁西区' ,2)

  33. go

从上面可以看出,递归的过程就是使用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语法如下:

  1. [ WITH <common_table_expression> [ ,n ] ]

  2. <common_table_expression>::=

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

  4. AS (

  5. CTE_query_definition1 -- 定位点成员(也就是初始值或第一个结果集)

  6. union all

  7. CTE_query_definition2 -- 递归成员

  8. )

三、CTE 使用示例

1. 查询节点及其下所有子节点

  1. with

  2. district as

  3. (

  4. -- 获得第一个结果集,并更新最终结果集

  5. select * from t_tree where node_name= N'辽宁省'

  6. union all

  7. -- 下面的select语句首先会根据从上一个查询结果集中获得的id值来查询parent_id

  8. -- 字段的值,然后district就会变当前的查询结果集,并继续执行下面的select 语句

  9. -- 如果结果集不为null,则与最终的查询结果合并,同时用合并的结果更新最终的查

  10. -- 询结果;否则停止执行。最后district的结果集就是最终结果集。

  11. select a.* from t_tree a, district b

  12. where a.parent_id = b.id

  13. )

  14. select * from district

2. 查询有子节点的节点

  1. with

  2. district as

  3. (

  4. select * from t_tree where node_name= N'辽宁省'

  5. union all

  6. select a.* from t_tree a, district b

  7. where a.parent_id = b.id

  8. ),

  9. district1 as

  10. (

  11. select a.* from district a where a.id in (select parent_id from district)

  12. )

  13. select * from district1

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

3.使用 MAXRECURSION 来防止不合理的递归

  1. ;with district as (

  2. select * from t_tree where node_name= N'辽宁省'

  3. union all

  4. select a.* from t_tree a, district b

  5. where a.id = b.id -- 故意制造无限循环

  6. )

  7. select * from district

  8. OPTION (MAXRECURSION 5) -- 限制特定语句所允许的递归级数,以防止出现无限循环

4. with as 与 insert 结合使用

  1. if exists (

  2. select *

  3. from sys.objects

  4. where name = 'fn_Test'

  5. and type in ('FN' ,'TF')

  6. )

  7. drop function dbo.fn_Test;

  8. go

  9. create function dbo.fn_Test

  10. (

  11. @nodename varchar(50)

  12. )

  13. returns @result table (

  14. id int not null

  15. ,node_name varchar(50) null

  16. ,parent_id int not null

  17. ,[description] varchar(255) null

  18. )

  19. as

  20. begin

  21. ;with district as (

  22. select *

  23. from t_tree where node_name = @nodename

  24. union all

  25. select a.*

  26. from t_tree a, district b

  27. where a.parent_id = b.id

  28. ),

  29. district1 as (

  30. select *

  31. from district a

  32. where a.id in (select parent_id

  33. from district)

  34. )

  35. insert into @result

  36. (

  37. id

  38. ,node_name

  39. ,parent_id

  40. ,[description]

  41. )

  42. select id

  43. ,node_name

  44. ,parent_id

  45. ,[description]

  46. from district1;

  47. return ;

  48. end;

  49. go

四、递归 CTE 使用注意事项

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

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

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

8. 如果递归 CTE 组合不正确,可能会导致无限循环。例如,如果递归成员查询定义对父列和子列返回相同的值,则会造成无限循环。可以使用 MAXRECURSION 提示以及在 INSERT、UPDATE、DELETE 或 SELECT 语句的 OPTION 子句中的一个 0 到 32,767 之间的值,来限制特定语句所允许的递归级数,以防止出现无限循环。这样就能够在解决产生循环的代码问题之前控制语句的执行。服务器范围内的默认值是 100。如果指定 0,则没有限制。每一个语句只能指定一个 MAXRECURSION 值。 
9. 不能使用包含递归公用表表达式的视图来更新数据。 
10. 可以使用 CTE 在查询上定义游标。递归 CTE 只允许使用快速只进游标和静态(快照)游标。如果在递归 CTE 中指定了其他游标类型,则该类型将转换为静态游标类型。

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

SQL Server with as使用相关推荐

  1. 【Sql Server】数据库的3大服务

    在数据库SQL SERVER中,处理常用的sql server数据库引擎,还有其他3大服务,分别是集成服务,报表服务,分析服务. 集成服务商可以配置包,这里的包可以理解是数据库引擎里的用户数据库.可以 ...

  2. 【Sql Server】DateBase-自动化

    强大的SQL Server有着神奇的自动化功能,来处理一些人为处理不了的事情! 自动化功能:T-sql语言,系统命令.脚本语言.复制命令.创建角色.索引重建.报表 管理元素:作业.警报.操作员.计划 ...

  3. 【Sql Server】DateBase-视频总结

    最近看完了数据库视频,内容繁杂,但也不是无迹可寻! 这是第三遍关于数据库的学习了,随着一遍一遍的深入,更加了解了它的全貌,虽然现在对于数据库还不能到达熟练操作的地步,但至少放我手上不在犯怵了. SQL ...

  4. sql server登录名、服务器角色、数据库用户、数据库角色、架构区别联系

    原创链接:https://www.cnblogs.com/lxf1117/p/6762315.html sql server登录名.服务器角色.数据库用户.数据库角色.架构区别联系 1.一个数据库用户 ...

  5. 合肥工业大学—SQL Server数据库实验十:用户及其权限管理

    用户及其权限管理 1. 创建登录名Mylog及密码 2. 创建用户user2关联登录名 3. 创建角色role1 4. 对用户user2及角色role1授权 5. 验证用户授权 6. 收回用户权限 1 ...

  6. 合肥工业大学—SQL Server数据库实验四:数据库的分离和附加

    数据库的分离和附加 1. 数据库分离 2. 数据库附加 1. 数据库分离 当SQL Server服务器运行时,该服务器上所有的数据库自动处于运行状态,而运行中的数据库文件是无法进行数据库文件的拷贝的. ...

  7. SQL Server中Identity标识列

    SQL Server中,经常会用到Identity标识列,这种自增长的字段操作起来的确是比较方便.但它有时还会带来一些麻烦. SQL Server中,经常会用到Identity标识列,这种自增长的字段 ...

  8. SQL SERVER 函数ROW_NUMBER() 应用

    应用场景: 1.业务要求SQL查询结果中输出含有"序号"."行号". 2.数据表中某个空白字段需要用流水号填充. 以上场景时,可以使用SQL SERVER 的函 ...

  9. java实体属性对应mysql和SQL Server 和Oracle 数据类型对应

    1:Java数据类型与MySql数据类型对照表 类型名称 显示长度 数据库类型 JAVA类型 JDBC类型索引(int) VARCHAR L+N VARCHAR java.lang.String 12 ...

  10. SQL Server Extended Events 进阶 3:使用Extended Events UI

    开始采用Extended Events 最大的阻碍之一是需要使用Xquery和XML知识用来分析数据.创建和运行会话可以用T-SQL完成,但是无论使用什么目标,数据都会被转换为XML.这个限制在SQL ...

最新文章

  1. Spring Cloud Edgware新特性之一:解决Eureka中Jersey 1.x版本过旧的问题-不使用Jersey
  2. 计算机包括桌面计算机和便携式计算机,()计算机包括桌面计算机和便携式计算机这两种形式...
  3. Spring Boot快速开发企业级Admin管理后台
  4. mac系统访问群晖服务器地址,获取发现访问 U-NAS 系统管理中心 IP 地址方法
  5. 在C语言的函数后标注small,大佬在吗,我用C写了一个去多重括号的函数,结果。。。...
  6. leetcode - 1201. 丑数 III
  7. 设计师必备的资源网站和灵感作品社区分享!
  8. NestedScrollView、RecycleView、ViewPager 嵌套常见问题
  9. SWFUpload flash上传控件
  10. 甲骨文裁员后应该怎样发展后来的路?
  11. Unity之AB包的创建加载
  12. SDAU信息学院LaTeX模板使用指南
  13. 航班经停地查询api 航班经停地及起降时间查询
  14. 资产证券化为什么需要区块链技术?专访趣链科技揭开“区块链+ABS”迷雾
  15. gazebo设置_gazebo教程(六)插件配置
  16. amcharts php,分享四个amCharts使用中的问题研究
  17. 我在华清的嵌入式课程笔记(至2020.7.5)
  18. ELF文件格式, ELF文件是什么,里面包含什么内容
  19. 浙大计算机城市学院联合培养,浙大城市学院 今日视点 城市学院2007届联合培养硕士研究生顺利毕业...
  20. 美专家起诉欧洲对撞机可能撞出黑洞毁灭地球

热门文章

  1. python、Linux、MySQL学习笔记
  2. R语言基础 chapter2
  3. Java-Tomcat如何修改端口号
  4. 5G NR 随机接入RACH流程(1)-- 概述
  5. linux如何解除密码锁屏图案大全,手机锁屏图案(锁屏密码)忘记了怎么办?四种方法帮你轻松搞定...
  6. 数学智力题 武士数独题目_这6道数学智力题,我琢磨一上午也无解,结果被一个学霸全部搞定...
  7. 操作系统 时间片轮转调度算法
  8. 用echarts做风速风向折线图
  9. 面试题:MySQL优化
  10. docker的下载与安装