概述

  表表达式是一种命名的查询表达式,代表一个有效地关系表。可以像其他表一样,在数据处理中使用表表达式。

  SQL Server支持四种类型的表表达式:派生表公用表表达式视图内联表值函数

为什么使用表表达式:

  1.使用表表达式的好处是逻辑方面,在性能上没有提升。

  2.通过模块化的方法简化问题的解决方案,规避语言上的某些限制。在外部查询的任何字句中都可以引用在内部查询的SELECT字句中分配的列别名。比如在SELECT字句中起的别名,不能在WHERE,group by等字句(逻辑顺序位于SELECT字句之前的字句)中使用,通过表表达式可以解决这类问题

派生表

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

派生表可以简化查询,避免使用临时表。相比手动生成临时表性能更优越。派生表与其他表一样出现在查询的FROM子句中

select * from (select * from athors)  temp

temp 就是派生表

派生出来的表必须要是一个有效的表.因此,它必须遵守以下几条规则:

1. 所有列必须要有名称

2. 列名称必须是要唯一

3. 不允许使用ORDER BY(除非指定了TOP)

派生表:比如要查找一个叫张铁牛的人的信息,我们知道他是男性,为了缩小查找范围我把所有的男性都找出来,然后从这些男性中里面再去找张铁牛.这里男性的集合就相当于派生表,转成sql语句:

select 姓名,住址,身份证

from (select * from 表名 where 性别='男性') temp 

where 姓名='张铁牛'

(这里只是为了举例子),这里的 temp这个数据集就是派生表,它是虚表,在数据库中不存在的,是我们构建的,在这里的目的是为了缩小数据的查找范围.

分配列别名(建议使用内嵌别名形式)

SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM (SELECT YEAR(orderdate), custid
FROM Sales.Orders) AS D(orderyear, custid)
GROUP BY orderyear;

使用参数

DECLARE @empid AS INT = 3;

SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM (SELECT YEAR(orderdate) AS orderyear, custid
FROM Sales.Orders
WHERE empid = @empid) AS D
GROUP BY orderyear;

 嵌套

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

例子:查询每年处理客户数超过70的订单年度和每年所处理的客户数量。

方案一:我们用第一节中单表查询查询出结果

SELECT YEAR(orderdate) AS orderyear, COUNT(DISTINCT custid) AS numcusts
FROM Sales.Orders
GROUP BY YEAR(orderdate)
HAVING COUNT(DISTINCT custid) > 70;

方案二:嵌套派生表

SELECT orderyear, numcusts
FROM (SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM (SELECT YEAR(orderdate) AS orderyear, custid
FROM Sales.Orders) AS D1
GROUP BY orderyear) AS D2
WHERE numcusts > 70;

嵌套查询看起来非常复杂,嵌套查询也是很容易产生问题的一个方面。在这个例子中,使用嵌套派生表的目的是为了重用列别名。但是,由于嵌套增加了代码的复杂性,所以对于本例考虑使用方案一。

多个引用

SELECT Cur.orderyear,
Cur.numcusts AS curnumcusts, Prv.numcusts AS prvnumcusts,
Cur.numcusts - Prv.numcusts AS growth
FROM (SELECT YEAR(orderdate) AS orderyear,
COUNT(DISTINCT custid) AS numcusts
FROM Sales.Orders
GROUP BY YEAR(orderdate)) AS Cur
LEFT OUTER JOIN
(SELECT YEAR(orderdate) AS orderyear,
COUNT(DISTINCT custid) AS numcusts
FROM Sales.Orders
GROUP BY YEAR(orderdate)) AS Prv
ON Cur.orderyear = Prv.orderyear + 1;

缺点:我们看到其实我们是对同一张表进行联接,但是需要将代码定义多遍,这无疑造成代码的混乱和冗余。

公用表表达式(CTE)——推荐

公用表表达式是和派生表相似的另一种形式的表表达式,但是公用表表达式具有一些优势。

公用表表达式和派生表一样,前面需要遵守的规则对公用表表达式同样适用。当外部查询结束,公用表表达式的生命周期就结束了。

其实CTE的作用就相当于子查询。

内联格式:别名写在内部查询中

WITH C AS
(
SELECT YEAR(orderdate) AS orderyear, custid
FROM Sales.Orders
)
SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM C
GROUP BY orderyear;

外联格式:列的别名写在外部查询中

WITH C(orderyear, custid) AS
(
SELECT YEAR(orderdate), custid
FROM Sales.Orders
)
SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM C
GROUP BY orderyear;

使用参数

DECLARE @empid AS INT = 3;

WITH C AS
(
SELECT YEAR(orderdate) AS orderyear, custid
FROM Sales.Orders
WHERE empid = @empid
)
SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM C
GROUP BY orderyear;

定义多个CTE

CTE和派生表相关具有以下优势:

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

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

WITH C1 AS
(
SELECT YEAR(orderdate) AS orderyear, custid
FROM Sales.Orders
),
C2 AS
(
SELECT orderyear, COUNT(DISTINCT custid) AS numcusts
FROM C1
GROUP BY orderyear
)
SELECT orderyear, numcusts
FROM C2
WHERE numcusts > 70;

CET中的多个引用

公用表表达式的好处之一是可以在接下来一条语句中多次引用:

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 YearlyCount AS
(
SELECT YEAR(orderdate) AS orderyear,
COUNT(DISTINCT custid) AS numcusts
FROM Sales.Orders
GROUP BY YEAR(orderdate)
)
SELECT Cur.orderyear,
Cur.numcusts AS curnumcusts, Prv.numcusts AS prvnumcusts,
Cur.numcusts - Prv.numcusts AS growth
FROM YearlyCount AS Cur
LEFT OUTER JOIN YearlyCount AS Prv
ON Cur.orderyear = Prv.orderyear + 1;

递归CET

WITH EmpsCTE AS
(
SELECT empid, mgrid, firstname, lastname
FROM HR.Employees
WHERE empid = 2 UNION ALL SELECT C.empid, C.mgrid, C.firstname, C.lastname
FROM EmpsCTE AS P
JOIN HR.Employees AS C
ON C.mgrid = P.empid
)
SELECT empid, mgrid, firstname, lastname
FROM EmpsCTE;

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

CET、临时表、表变量

CET优点:

  相对于派生表最主要的优势在于可以一次定义,多次使用。

  CET大部分地方可以代替临时表。CTE最优秀的地方是在实现递归操作,和替代绝大部分游标的功能。

  CET后面必须直接跟使用CTE的SQL语句(如select、insert、update等),否则,CET将失效。但是临时表一直存在,除非drop掉。

CET缺点:

  对于大数据量,由于CET不能建索引,所以明显比临时表差。我给开发的建议是少于1万数据的话,CET和表变量就不要用于做暂存数据的功能。而改用临时表。

  数据量大时,CET的性能要比临时表差很多(即使临时表不建索引)

  CET要比表变量效率高得多!

临时表和表变量的选择

  我们对于较小的数据或者是通过计算出来的推荐使用表变量。如果数据的结果比较大,在代码中用于临时计算,在选取的时候没有什么分组的聚合,就可以考虑使用表变量。

  一般对于大的数据结果,或者因为统计出来的数据为了便于更好的优化,我们就推荐使用临时表,同时还可以创建索引,由于临时表是存放在Tempdb中,一般默认分配的空间很少,需要对tempdb进行调优,增大其存储的空间。

  表变量实际上使用了临时表,从而增加了额外的I/O开销,因此,表变量的方式并不太适合数据量大且频繁查询的情况

视图

1.视图和派生表和CTE的区别和共同点

区别:

  派生表和CTE不可重用:只限于在单个语句的范围内使用,只要包含这些表表达式的外部查询完成操作,它们就消失了。

  视图和内联表值函数是可重用的:它们的定义存储在一个数据对象中,一旦创建,这些对象就是数据库的永久部分;只有用删除语句显示删除或用右键删除,它们才会从数据库中移除。

共同点:

  在很多方面,视图和内联表值函数的处理方式都类似于派生表和CTE。当查询视图和内联表值函数时,SQL Server会先扩展表表达式的定义,再直接查询底层对象。

创建一个视图:

IF OBJECT_ID('Sales.USACusts') IS NOT NULL
DROP VIEW Sales.USACusts;
GO
CREATE VIEW Sales.USACusts
AS
SELECT
custid, companyname, contactname, contacttitle, address,
city, region, postalcode, country, phone, fax
FROM Sales.Customers
WHERE country=N'USA';
GO

使用该视图:

SELECT * FROM Sales.USACusts;

内联表值函数(内嵌TVF)

什么是内联表值函数

一种可重用的表表达式,能够支持输入参数。除了支持输入参数以外,内联表值函数在其他方面都与视图相似。

内嵌表值函数是支持输入参数的可重复使用的表表达式。除了支持输入参数之外的其他所有方面都和视图类似。我们来看下怎么创建内嵌表值函数。

下面演示如何创建函数:

IF OBJECT_ID('dbo.fn_GetCustOrders') IS NOT NULL
DROP FUNCTION dbo.fn_GetCustOrders;
GO
CREATE FUNCTION dbo.fn_GetCustOrders
(@cid AS INT) RETURNS TABLE
AS
RETURN
SELECT
orderid, custid, empid, orderdate, requireddate,
shippeddate, shipperid, freight, shipname, shipaddress, shipcity,
shipregion, shippostalcode, shipcountry
FROM Sales.Orders
WHERE custid=@cid;
GO

如何使用函数:

SELECT orderid, custid
FROM dbo.fn_GetCustOrders(1) AS CO;

 小结

  借助表表达式可以简化代码,提高代码的可维护性,还可以封装查询逻辑。

  当需要使用表表达式,而且不计划重用它们的定义时,可以使用派生表或CTE。与派生表相比,CTE具有两个优点:CTE不用像派生表那样嵌套使用,此外,还可以引用同一CTE的多个实例,也派生表不能这么用。

  当需要定义可重用的表表达式时,可以使用视图和内联表值函数。如果不须要支持输入参数,则使用视图,相反则使用内联表值函数。

  (1)表表达式可以简化代码,提高代码的可维护性和封装查询逻辑。

  (2)当需要使用表表达式并且不打算重复使用其定义时,可以使用派生表或CTE,而CTE对派生表具有更多优势不需要像派生表那样嵌套CTE,使用CTE使代码更加模块化和便于维护,此外,还可以引用同一个CTE的多个实例,这一点是派生表无法实现的。

  (3)当需要使用表表达式并且需要定义可重复使用的表表达式时,可以使用视图或内嵌表值函数,当不需要支持输入参数时,可以使用视图,否则,应当使用内嵌表值函数(TVF)。

APPLY运算符

APPLY运算符是一个非标准标准运算符。APPLY运算符对两个输入进行操作,其中右边的表可以是一个表表达式。

CROSS APPLY把右边表达式应用到左表中的每一行,再把结果集组合起来,生成一个统一的结果表。和交叉连接相似

OUTER APPLY把右边表达式应用到左表中的每一行,再把结果集组合起来,然后添加外部行。和左外联接中增加外部行的那一步相似

资料

https://www.cnblogs.com/kissdodog/archive/2013/06/24/3153012.html

https://blog.csdn.net/miqi770/article/details/51505720

https://www.cnblogs.com/janneystory/p/5623019.html

http://www.cnblogs.com/jackson0714/p/TSQLFundamentals_04_part1.html

SQL Server 表表达式--派生表、公用表表达式(CTE)、视图和内联表值函数的更多相关文章

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

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

  2. SQL Server标量函数改写内联表值函数优化案例

    问题SQL: SELECT TOP 1001 ha.HuntApplicationID , ha.PartyNumber , mht.Name AS MasterHuntTypeName , htly ...

  3. sql server 创建内联表值函数

    表值函数就是返回table 的函数使用它可以方便的进行查询的处理 创建的代码如下: create FUNCTION returunclassfirstlist(  -- Add the paramet ...

  4. [sql Server]除非另外还指定了TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效

    今天遇到一个奇怪的问题,项目突然要从mysql切换到sql server数据库,包含order by 子句的嵌套子查询报错. 示例:select top 10 name,age,sex from ( ...

  5. 【SQL Server】系统学习之一:表表达式

    本节讨论的相关内容包括:视图.派生表.CTE.内联表值函数 场景:如果要查询一组数据(例如聚合数据,也就是几个表聚合在一起的数据),这些数据并未在数据库中以表的形式存在. 1.视图:通常用来分解大型的 ...

  6. SQL Server 判断数据库中是否存在表

    使用场景 可以反复的执行相同脚本 方式1:查询sysobjects表 if EXISTS (SELECT * from sysobjects WHERE name='test_table') DROP ...

  7. sql:除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询

    执行sql语句: select * from ( select * from tab where ID>20 order by userID desc ) as a order by date ...

  8. [转]sql:除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询

    执行sql语句: select * from ( select * from tab where ID>20 order by userID desc ) as a order by date ...

  9. sql server 查询多个不关联表且对结果编号

    1.除非另外还指定了 TOP 或 FOR XML,否则,ORDER BY 子句在视图.内联函数.派生表.子查询和公用表表达式中无效. 解决方法:top 100 percent * 2.如何对查询结果编 ...

随机推荐

  1. PAT(B) 1043 输出PATest(Java)统计

    题目链接:1043 输出PATest (20 point(s)) 题目描述 给定一个长度不超过 10​4​​ 的.仅由英文字母构成的字符串.请将字符重新调整顺序,按 PATestPATest- 这样的 ...

  2. access函数的使用

    #include<unistd.h> int access(const char* pathname, int mode); 参数介绍: pathname 是文件的路径名+文件名 mode ...

  3. 5-24 c++语言之【基础知识】

    最近一段时间继续开始了c++的学习,作为c plus plus 难免会与c语言做一个对比,很明显的感受到c++语言注重代码的复用性和拓展性,而c语言更加注重其算法的高效性,这也是今后需要注意的地方,避 ...

  4. 二十、网卡框架分析、虚拟网卡驱动和DM9621驱动分析

    一.网络设备驱动的结构 网卡设备不同于字符设备和块设备, 网络设备并不对应于/dev目录下的文件,它存放在/sys/class/net目录下. Linux系统对网络设备驱动定义了四个层次: 1. 网络 ...

  5. Qt更新组件出现(“要继续此操作,至少需要一个有效且已启用的储存库”)

    Qt更新组件出现(“要继续此操作,至少需要一个有效且已启用的储存库”)   目的: 当时在安装Qt时,有些组件暂时没用着,然后过一段时间后,需要用到某些该组件时,不用删掉重新再安装. 操作: Wind ...

  6. sqlserver 统计每分钟内的数量

    1.统计每分钟内 url 的访问数量 SELECT SUBSTRING(CONVERT(varchar(100), date, 20), 0,17) as dateTime,COUNT(url) as ...

  7. php 判断请求是否是json

    $object =file_get_contents("php://input"); $arr = is_json($object); if($arr){ var_dump($ar ...

  8. 数据结构之链表(LinkedList)(二)

    数据结构之链表(LinkedList)(一) 双链表 上一篇讲述了单链表是通过next 指向下一个节点,那么双链表就是指不止可以顺序指向下一个节点,还可以通过prior域逆序指向上一个节点 示意图: ...

  9. 【转载】 C#使用string.Join快速用特定字符串串联起数组

    在C#中有时候我们的数组元素需要通过一些特定的字符串串联起来,例如将整形Int数组通过逗号快速串联起来成为一个字符串,可以使用String.Join方法.或者一个字符串string类型数组所有元素快速 ...

  10. 【转载】IIS网站配置不带www域名直接跳转带www的域名

    很多时候为了统一网站入口,需要将不带www的主域名解析到带www的域名记录下,当客户访问不带www的域名网址的时候自动跳转到带www的域名,在IIS Web服务器中可以通过URL重写模块来实现此功能, ...