表表达式

Microsoft SQL Server支持4种类型的表表达式:派生表(derived table),公用表表达式(CTE,common table expression),视图,以及内联表值函数(inline TVF,inline table-valued function).

(重点介绍表表达式在SELECT查询中的用法).

表表达式并不是物理上真实存在的什么对象,它们是虚拟的.使用表表达式的好处通常体现在代码的逻辑方面,而不是性能方面.

(联接表表达式的APPLY表运算符;如何用APPLY这个运算符为另一个表的每一行来应用某个表表达式)

要有效地定义任何类型的表表达式,查询语句必须满足三个要求:

1.不保证有一定的顺序.

表表达式代表的是一个表(无序的).ANSI SQL不允许在用于定义表表达式的查询语句中有ORDER BY子句.(TOP除外!在带有TOP选项的查询语句中,ORDER BY子句的逻辑目的只有一个:为TOP选项定义要筛选出哪些行.如果用带有TOP和ORDER BY子句的查询语句来定义表表达式,ORDER BY保证只为TOP选项提供逻辑筛选数据的服务,而不用于通常数据展示的目的.)

2.所有的列必须有名称.

表中的所有列必须有列名,在用于定义表表达式的查询语句中,必须为SELECT列表中的表达式起别名.

3.所有的列名必须是唯一的.

表中所有的列名必须是唯一的.如果表表达式中有多个列具有相同的名称,则该表表达式是无效的.(如果确实需要在表表达式中包括这两个同名列,它们就必须具有不同的列名,可以为这两个列起不同的列别名)

5.1 派生表

派生表(也称为表子查询)是在外部查询的FROM子句中定义的.派生表的存在范围为定义它的外部查询,只要外部查询一结束,派生表也就不存在了.

派生表的定义

  1. ---------------------------------------------------------------------
  2. -- Derived Tables
  3. ---------------------------------------------------------------------
  4.  
  5. --定义派生表的查询语句要写在一对圆括号内,后面跟着AS子句和派生表的名称...
  6. USE TSQLFundamentals2008;
  7.  
  8. SELECT *
  9. FROM (SELECT custid, companyname
  10. FROM Sales.Customers
  11. WHERE country = N'USA') AS USACusts;
  12. GO

5.1.1 分配列别名

使用表表达式的一个好处是:在外部查询的任何子句中都可以引用在内部查询的SELECT子句中分配的列别名.

一个限制:不能在逻辑处理顺序先于SELECT子句的其他查询子句(如WHERE或GROUP BY)中对SELECT子句分配的列别名进行引用!

  1. ---------------------------------------------------------------------
  2. -- Assigning Column Aliases
  3. ---------------------------------------------------------------------
  4.  
  5. -- Following fails
  6. --一个限制:不能在逻辑处理顺序先于SELECT子句的其他查询子句(如WHEREGROUP BY)中对SELECT子句分配的列别名进行引用!
  7. SELECT
  8. YEAR(orderdate) AS orderyear,
  9. COUNT(DISTINCT custid) AS numcusts
  10. FROM Sales.Orders
  11. GROUP BY orderyear;
  12. GO
  13.  
  14. -- Listing 5-1 Query with a Derived Table using Inline Aliasing Form
  15. -- 查询对象是一个名为D的表,这个表有两列:orderyearcustid,所以之后的都可以调用orderyear,custid
  16. -- 注:强调使用表表达式是出于逻辑原因,而与性能无法.一般来说,表表达式既不会对性能产生正面影响,也不会对性能产生负面影响.
  17. SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  18. FROM (SELECT YEAR(orderdate) AS orderyear, custid
  19. FROM Sales.Orders) AS D
  20. GROUP BY orderyear;
  21.  
  22. -- 可以在GROUP BYSELECT子句中都引用 YEAR(orderdate) 这个表达式...
  23. SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts
  24. FROM Sales.Orders
  25. GROUP BY YEAR(orderdate);
  26.  
  27. -- External column aliasing
  28. -- 在表表达式的名称后面,在一对圆括号中一次性指定所有目标列的名称...
  29. SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  30. FROM (SELECT YEAR(orderdate), custid
  31. FROM Sales.Orders) AS D(orderyear, custid)
  32. GROUP BY orderyear;
  33. GO
  34.  
  35. --内联别名格式:(SELECT YEAR(orderdate) AS orderyear, custid FROM Sales.Orders) AS D
  36. --外部命名格式:(SELECT YEAR(orderdate), custid FROM Sales.Orders) AS D(orderyear, custid)
  37.  
  38. --通常建议使用内联别名格式...
  39. --原因:1.使用内联格式时,如果须要调试代码,只要把定义表表达式的查询语句独立出来,再运行,在结果中出现的列名就是原来指定的列名.
  40. -- 2.使用外部格式,当把表表达式查询独立出来后,就不能在其中包含目标列名,这时如果查询中有未命名的表达式,在结果中这些表达式对应的列就没有列名.
  41. -- 当表表达式的查询很长,采用外部命名格式可能很难分辨出列别名所属的表达式.
  42. --即使使用内联别名格式是最佳实践方法,在某些情况下,外部命名格式可能用起来更方便.

5.1.2 使用参数

在定义派生表的查询中,可以引用参数.参数可以是局部变量和例程(如存储过程或函数)的输入参数.

  1. ---------------------------------------------------------------------
  2. -- Using Arguments
  3. ---------------------------------------------------------------------
  4.  
  5. --以下这个查询返回指定雇员每年处理的订单所属的不同客户数量,输入的雇员ID保存在变量@empid之中.
  6. -- Yearly Count of Customers handled by Employee 3
  7. DECLARE @empid AS INT = 3;
  8.  
  9. /*
  10. -- Prior to SQL Server 2008 use separate DECLARE and SET statements:
  11. DECLARE @empid AS INT;
  12. SET @empid = 3;
  13. */
  14.  
  15. SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  16. FROM (SELECT YEAR(orderdate) AS orderyear, custid
  17. FROM Sales.Orders
  18. WHERE empid = @empid) AS D
  19. GROUP BY orderyear;
  20. GO

5.1.3 嵌套

如果须要用一个本身就引用了某个派生表的查询去定义另一个派生表,最终得到的就是嵌套派生表.

  1. ---------------------------------------------------------------------
  2. -- Nesting
  3. ---------------------------------------------------------------------
  4.  
  5. -- Listing 5-2 Query with Nested Derived Tables
  6. --以下两个查询:都是返回订单年份和该年处理的客户数,但要求每个订单年份处理的客户数要多于70人.
  7. --一个是嵌套派生表,一个是分组查询
  8. SELECT orderyear, numcusts
  9. FROM (SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  10. FROM (SELECT YEAR(orderdate) AS orderyear, custid
  11. FROM Sales.Orders) AS D1
  12. GROUP BY orderyear) AS D2
  13. WHERE numcusts > 70;
  14.  
  15. SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts
  16. FROM Sales.Orders
  17. GROUP BY YEAR(orderdate)
  18. HAVING COUNT(DISTINCT custid) > 70;

5.1.4 派生表的多引用

派生表另一个存在问题的方面源于派生表是在外部查询的FROM子句中定义的,其逻辑处理顺序并不优先于外部查询.当对外部查询的FROM子句进行处理时,派生表其实并不存在.因此,如果须要引用派生表的多个实例,这时还不能这样做.相反,必须基于同一查询去定义多个派生表.

  1. ---------------------------------------------------------------------
  2. -- Multiple References
  3. ---------------------------------------------------------------------
  4.  
  5. -- Listing 5-3 Multiple Derived Tables Based on the Same Query
  6. --以下查询:第一个派生表(Cur)代表当前年份,另一个派生表(Prv)代表上一年份.联接条件:Cur.orderyear = Prv.orderyear+1,用于说明第一个派生表的每一行怎么和第二个派生表的上一年进行匹配.通过采用左联接,第一年虽然没有上一年,但也会从Cur表中返回.外部查询的SELECT子句计算了当前年份和上一年份处理过的客户数量之差.
  7. SELECT Cur.orderyear,
  8. Cur.numcusts AS curnumcusts, Prv.numcusts AS prvnumcusts,
  9. Cur.numcusts - Prv.numcusts AS growth
  10. FROM (SELECT YEAR(orderdate) AS orderyear,
  11. COUNT(DISTINCT custid) AS numcusts
  12. FROM Sales.Orders
  13. GROUP BY YEAR(orderdate)) AS Cur
  14. LEFT OUTER JOIN
  15. (SELECT YEAR(orderdate) AS orderyear,
  16. COUNT(DISTINCT custid) AS numcusts
  17. FROM Sales.Orders
  18. GROUP BY YEAR(orderdate)) AS Prv
  19. ON Cur.orderyear = Prv.orderyear + 1;

5.2 公用表表达式(CTE)

公用表表达式(CTE,Common table expression)是和派生表很相似的另一种形式的表表达式,而且具有一些重要优势...

  1. ---------------------------------------------------------------------
  2. -- Common Table Expressions
  3. ---------------------------------------------------------------------
  4.  
  5. --CTE是用WITH子句定义的
  6. WITH USACusts AS
  7. (
  8. SELECT custid, companyname
  9. FROM Sales.Customers
  10. WHERE country = N'USA'
  11. )
  12. SELECT * FROM USACusts; --外部查询 ! 和派生表一样,一旦外部查询完成,CTE的生命期也就结束了.

5.2.1 分配列别名

CTE也支持两种格式的列别名命名方式--内联格式和外部格式.

内联格式: 指定<expression> as <column_alias>

外部格式: 在CTE名称后面的一对圆括号中指定目标列的列表.

  1. ---------------------------------------------------------------------
  2. -- Assigning Column Aliases
  3. ---------------------------------------------------------------------
  4.  
  5. -- Inline column aliasing
  6. -- 内联格式:指定<expression> as <column_alias>
  7. WITH C AS
  8. (
  9. SELECT YEAR(orderdate) AS orderyear, custid
  10. FROM Sales.Orders
  11. )
  12. SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  13. FROM C
  14. GROUP BY orderyear;
  15.  
  16. -- External column aliasing
  17. -- 外部格式:在CTE名称后面的一对圆括号指定目标列的列表
  18. WITH C(orderyear, custid) AS
  19. (
  20. SELECT YEAR(orderdate), custid
  21. FROM Sales.Orders
  22. )
  23. SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  24. FROM C
  25. GROUP BY orderyear;
  26. GO

5.2.2 使用参数

  1. ---------------------------------------------------------------------
  2. -- Using Arguments
  3. ---------------------------------------------------------------------
  4.  
  5. DECLARE @empid AS INT = 3;
  6.  
  7. /*
  8. -- Prior to SQL Server 2008 use separate DECLARE and SET statements:
  9. DECLARE @empid AS INT;
  10. SET @empid = 3;
  11. */
  12.  
  13. WITH C AS
  14. (
  15. SELECT YEAR(orderdate) AS orderyear, custid
  16. FROM Sales.Orders
  17. WHERE empid = @empid
  18. )
  19. SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  20. FROM C
  21. GROUP BY orderyear;
  22. GO

5.2.3 定义多个CTE

如果须要在一个CTE中引用另一个CTE,不须要像派生表那样进行嵌套,相反,只要简单地在同一WITH子句中定义多个CTE,并用逗号把它们分隔开.每个CTE可以引用在它前面定义的所有CTE,而外部查询则可以引用所有CTE.

  1. ---------------------------------------------------------------------
  2. -- Defining Multiple CTEs
  3. ---------------------------------------------------------------------
  4.  
  5. --CTE引用另一回CTE,不须要像派生表那样进行嵌套;只需在同一WITH子句中定义多个CTE,并用逗号把它们分隔开.
  6. --每个CTE可以引用在它前面定义的所有CTE,而外部查询则可以引用所有CTE.
  7. WITH C1 AS
  8. (
  9. SELECT YEAR(orderdate) AS orderyear, custid
  10. FROM Sales.Orders
  11. ),
  12. C2 AS
  13. (
  14. SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
  15. FROM C1
  16. GROUP BY orderyear
  17. )
  18. SELECT orderyear, numcusts
  19. FROM C2
  20. WHERE numcusts > 70;

5.2.4 CTE的多引用

  1. CTE是先定义,再查询,就外部查询的FROM子句来说,CTE是已经存在的.因此,可以引用同一个CTE的多个实例.
  1. ---------------------------------------------------------------------
  2. -- Multiple References
  3. ---------------------------------------------------------------------
  4.  
  5. --CTE是先定义,再查询,就外部查询的FROM子句来说,CTE是已经存在的.因此,可以引用同一个CTE的多个实例.
  6. WITH YearlyCount AS
  7. (
  8. SELECT YEAR(orderdate) AS orderyear,
  9. COUNT(DISTINCT custid) AS numcusts
  10. FROM Sales.Orders
  11. GROUP BY YEAR(orderdate)
  12. )
  13. SELECT Cur.orderyear,
  14. Cur.numcusts AS curnumcusts, Prv.numcusts AS prvnumcusts,
  15. Cur.numcusts - Prv.numcusts AS growth
  16. FROM YearlyCount AS Cur
  17. LEFT OUTER JOIN YearlyCount AS Prv
  18. ON Cur.orderyear = Prv.orderyear + 1;
  19.  
  20. --表表达式通常对性能没有任何影响,因为它们没有任何物化的存在.SQL Server会对这个例子中的两处CTE引用进行扩展,在数据库引擎内部,这个查询有一个联接Orders表的两个实例的自联接,在联接之前每个实例都要扫描和聚合表数据.采用派生表的方法也须要经历同样的物理处理步骤.

5.2.5 递归CTE

  1. ---------------------------------------------------------------------
  2. -- Recursive CTEs (Advanced, Optional)
  3. ---------------------------------------------------------------------
  4. --*****递归CTE
  5. WITH EmpsCTE AS
  6. (
  7. SELECT empid, mgrid, firstname, lastname
  8. FROM HR.Employees
  9. WHERE empid = 2
  10.  
  11. UNION ALL
  12.  
  13. SELECT C.empid, C.mgrid, C.firstname, C.lastname
  14. FROM EmpsCTE AS P
  15. JOIN HR.Employees AS C
  16. ON C.mgrid = P.empid
  17. )
  18. SELECT empid, mgrid, firstname, lastname
  19. FROM EmpsCTE;

5.3 视图

视图的创建

  1. ---------------------------------------------------------------------
  2. -- Views Described
  3. ---------------------------------------------------------------------
  4.  
  5. -- 以下为视图的创建!
  6. -- Creating VUSACusts View
  7. -- 注:一般推荐在和视图有关的上下文中应该避免使用 SELECT * 语句.列是在编译视图时进行枚举的,新加的列可能不会自动加到视图中.
  8. -- 最好的开发实践就是在视图的定义中显示地列出需要的列名.如果在底层中添加了列,而且在视图中需要这些新加的列,则可以使用ALTER VIEW 语句对视图定义进行相应的修改.
  9. USE TSQLFundamentals2008;
  10. IF OBJECT_ID('Sales.USACusts') IS NOT NULL
  11. DROP VIEW Sales.USACusts;
  12. GO
  13. CREATE VIEW Sales.USACusts
  14. AS
  15.  
  16. SELECT
  17. custid, companyname, contactname, contacttitle, address,
  18. city, region, postalcode, country, phone, fax
  19. FROM Sales.Customers
  20. WHERE country = N'USA';
  21. GO
  22.  
  23. --查询语句!
  24. SELECT custid, companyname
  25. FROM Sales.USACusts;
  26. GO

111

第五章 表表达式 T-SQL语言基础的更多相关文章

  1. SQL语言基础和数据库操作

    Sql语言基础: 核心思想:我们自己构造一段查询的代码,然后添加到语句后,从而得到想要的某些数据. Mysql是一种开源数据库 APP Serv:Apache+php+mysql,相当于phpstud ...

  2. PL/SQL语言基础

    PL/SQL语言基础 进行PL/SQL编程前,要打开输出set serveroutput on 1.创建一个匿名PL/SQL块,将下列字符输出到屏幕:"My PL/SQL Block Wor ...

  3. SQL语言基础-基本概念

    SQL:IBM的圣约瑟(SanJose),SEQUEL 2(也就是现在的SQL语言) 1979.Oracle首先提出提供了商用的SQL语言 1986.10美国ANSI采用SQL作为关系数据库管理系统的 ...

  4. orcale 之 SQL 语言基础

    SQL 全称是结构化查询语句(Structure Query Language),是数据库操作的国际化语言,对所有的数据库产品都要支持. SQL 语言的分类 我们按照其功能可以大致分为四类: 数据定义 ...

  5. 浅谈PL/SQL语言基础

    在前面的学习中,我们大部分接触的都是SQL语言,但是,在实现复杂操作的时候,SQL语言就无能为力了,这时候就需要引入新的语言,PL/SQL语言就是对SQL语言的扩展,可以实现存储过程,函数等的创建.下 ...

  6. 学习笔记:oracle学习三:SQL语言基础之sql语言简介、用户模式

    目录 1.sql语言简介 1.1 sql语言特点 1.2 sql语言分类 1.3 sql语言的编写规则 2.用户模式 2.1 模式与模式对象 2.2 实例模式scott 本系列是作为学习笔记,用于记录 ...

  7. javascript第五章--函数表达式

    ① 递归 ② 闭包 ③ 模仿块级作用域 ④ 私有变量

  8. OCP认证之Oracle的SQL语言基础(一)

    一.Oracle命令类别 数据操纵语言(DML):select;insert;delete;update;merge 数据定义语言(DDL):create;alter;drop;truncate 事物 ...

  9. .NET面试题解析(11)-SQL语言基础及数据库基本原理

      系列文章目录地址: .NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引 本文内容涉及到基本SQL语法,数据的基本存储原理,数据库一些概念.数据优化等.抱砖引玉,权当一个综合复习! ...

随机推荐

  1. webpack搭建多页面系统(一):对webpack 构建工具的理解

    为什么使用webpack构建工具? 1.开发效率方面: 在一般的开发过程中,分发好任务后,每个人完成自己单独的页面,如果有的人开发完成之后,接手别人的任务,就有可能造成开发时候的冲突. 如果利用模块化 ...

  2. React Redux 与胖虎

    这是一篇详尽的 React Redux 扫盲文. 对 React Redux 已经比较熟悉的同学可以直接看 <React Redux 与胖虎他妈>. 是什么 React Redux 是 R ...

  3. LVS配置

    今天面试时,突然被面试官问到怎样用shell命令搞定某个文件夹下java代码行数的统计. 想了一下,基本思路就是找到这个文件夹下面的所有java文件,然后每个文件统计一下代码,外层套个for循环,叠加 ...

  4. 批量删除.svn文件夹、.svn文件

      使用svn进行版本控制,每个文件夹下都有.svn文件夹,有些项目在脱离svn版本控制之后,想删除项目中所有的.svn文件夹,可用下面的方法进行快速删除: 1.打开要删除.svn文件的最外层文件夹, ...

  5. Spring核心内容-认识bean

  6. 在已开启Chrome窗口上调试

    代码 @Test void testNow() { /* First: Add the chrome.exe to the PATH. * Then: open the cmd and input t ...

  7. leetcode常见算法与数据结构汇总

    leetcode刷题之后,很多问题老是记忆不深刻,因此特意开此帖: 一.对做过题目的总结: 二.对一些方法精妙未能领会透彻的代码汇总,进行时常学习: 三.总结面试笔试常见题目,并讨论最优解法及各种解法 ...

  8. 手把手教你 iOS通过自己的服务器实现应用分发

    第一步:打包ipa 1:可以是development.ad-hoc.enterprise任何一种打包方式,导出的ipa, 稍后会将安装包上传到服务器上. 2:如下图,箭头指的要打勾 3.点击下一步后出 ...

  9. Win10删除文件显示删除确认对话框

    1.右键单击“回收站”图标:2.在弹出属性窗口中,点击“属性”选项:3.在“回收站”窗口中,在选项“显示删除确认对话框”前面打钩,并单击“确定”按钮:

  10. openerp学习笔记 搜索视图(自己创建的、自己的、本部门的、本部门及下属部门的、今日的、日期从,日期至、多条件模糊搜索、or、and)

    自己创建的: domain="[('create_uid','=',uid)]" 自己的: domain="[('employee_id','=','#kl_user_e ...