zoukankan      html  css  js  c++  java
  • T-SQL 之 公用表表达式(CTE)

      在编写T-SQL代码时,往往需要临时存储某些结果集。在CTE之前常用的两种临时存储结果集为:临时表和表变量。除此之外,还可以使用公用表表达式的方法。

      公用表表达式(Common Table Expression,CTE)是SQL Server2005版本的引入的一个特性。CTE可以看作是一个临时的结果集,可以再接下来来的一个SELECT,INSERT,UPDATE,DELETE,MERGE 语句中多次引用。使用公用表达式CTE可以让语句更加清晰简练。

    一、三种方式的对比

      1、临时表:需要在临时数据库TempDB中通过I/O操作来创建表结构,一旦用户退出 SQL Server 环境则自动被删除。

      2、表变量:在内存中以表结构的形式存在,其定义与变量一致,其使用与表类似,不需要产生I/O。

      3、CTE:定义在内存中保存的临时存储结果集对象,不产生I/O,不需要按照表变量这样定义,使用方法和表类似。可以自己引用,也可以在查询中被多次引用。

    二、关键字“ WITH AS ”

      WITH AS-做子查询部分(subquery factoring)。

      它用于定义一个SQL片段,该片段会被是整个SQL语句所用到。如果WITH AS所定义的表名被调用两次以上,则优化器会自动将WITH AS所获取的数据放入临时表里,如果只是被调用一次,则不会。可以通过 materialize 将WITH AS短语里的数据强制放入全局临时表里。

      WITH AS可以被紧跟着的一条SQL语句所使用多次,但不能被紧跟着的多条SQL语句使用。

    WITH B AS 
    (
        SELECT * FROM xxx WHERE Id > 5
    )
    SELECT * FROM B

    三、CTE的定义

      CTE的定义语法如下,主要包括3个部分。

      1、Expression_name:CTE表达式的名称。

      2、Column_name:列名列表。

      3、CTE_query_definition:定义CTE结果集的 SEECT 查询语句

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

      根据微软对CTE好处的描述,可以归结为四点:

      [1] 可以定义递归公用表表达式(CTE);

      [2] 当不需要将结果集作为视图被多个地方引用时,CTE可以使其更加简洁;

      [3] GROUP BY语句可以直接作用于子查询所得的标量列;

      [4] 可以在接下来的一个 SELECT 语句中多次引用公用表表达式(CTE);

      按照是否递归,可以将公用表(CTE)表达式分为递归公用表表达式和非递归公用表表达式.

    三、非递归公用表表达式(CTE):

      非递归公用表表达式(CTE)是查询结果仅仅一次性返回一个结果集用于外部查询调用。并不在其定义的语句中调用其自身的CTE。

      非递归公用表表达式(CTE)的使用方式和视图以及子查询一致。

      比如一个简单的非递归公用表表达式:

    WITH CTE_Test
    AS
    (
        SELECT * FROM User
    )
    SELECT * FROM CTE_Test;

      CTE 可以在接下来一条语句中多次引用:

        WITH CTE_Test
      AS
      (
          SELECT * FROM User
      )
      SELECT * FROM CTE_Test AS a  --第一次引用
      INNER JOIN  CTE_Test AS b    --第二次引用
      ON a.Id = b.Id
      ORDER BY a.Id DESC

      以上引用了多次,但是都在一条 SELECT 语句中,可以正常执行。

      如果多条语句引用,如下面这样,是会报错的。

        WITH CTE_Test
      AS
      (
          SELECT * FROM User
      )
      SELECT * FROM CTE_Test; 
    
      SELECT * FROM CTE_Test; //对象“CTE_Test”不存在

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

    WITH CTE_Test1
    AS
    (
        SELECT * FROM table1
    ),
    CTE_Test2
    AS
    (
        SELECT * FROM table2
    )
    SELECT * FROM CTE_Test1
    UNION
    SELECT * FROM CTE_Test2

    四、递归公用表表达式(CTE):

      递归公用表表达式很像派生表(Derived Tables ),指的是在CTE内的语句中调用其自身的CTE。与派生表不同的是,CTE可以在一次定义多次进行派生递归。对于递归的概念,是指一个函数或是过程直接或者间接的调用其自身。要构成递归函数,需要两部分:第一部分是基础部分,返回固定值,也就是告诉程序何时开始递归;第二部分是循环部分,是函数或过程直接或者间接调用自身进行递归。

      对于递归公用表达式来说,实现原理也是相同的,同样需要在语句中定义两部分:

      [1] 基本语句;

      [2] 递归语句;

      先建一张表栏目表如下,栏目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 * FROM COL_CTE。这正是CTE强大的地方,当然,越强大的力量,就需要被约束。如果使用不当的话,递归CTE可能会出现无限递归。从而大量消耗SQL Server的服务器资源。因此,SQL Server提供了OPTION选项( OPTION(MAXRECURSION 2) ),可以设定最大的递归次数。

      如将上面的查询语法改为:

    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可以更加轻松愉快的用优雅简洁的方式实现复杂的查询。

  • 相关阅读:
    python基础之包、模块、命名空间和作用域
    python基础之函数式编程
    python基础之文件操作
    python基础之psutil模块和发邮件(smtplib和yagmail)
    【面试题21】包含min函数的栈
    【面试题20】顺时针打印矩阵
    【面试题19】二叉树的镜像
    【面试题18】树的子结构
    【面试题17】合并两个排序的链表
    【面试题16】反转链表
  • 原文地址:https://www.cnblogs.com/xinaixia/p/5816613.html
Copyright © 2011-2022 走看看