`
lin870521
  • 浏览: 17063 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

在SQL Server中,关于with as使用介绍

阅读更多

一.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语句如下:


with
cr as
(
    select CountryRegionCode from person.CountryRegion where Name like 'C%'
)

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:


with
cr as
(
    select CountryRegionCode from person.CountryRegion where Name like 'C%'
)
select * from person.CountryRegion -- 应将这条SQL语句去掉
-- 使用CTE的SQL语句应紧跟在相关的CTE后面 --
select * from person.StateProvince where CountryRegionCode in (select * from cr)


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


with
cte1 as
(
    select * from table1 where name like 'abc%'
),
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语句使用的就是数据表或视图了,如下面的SQL语句所示:


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

with
table1 as
(
    select * from persons where age < 30
)
select * from table1 -- 使用了名为table1的公共表表达式
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所示:

declare @s nvarchar(3)
set @s = 'C%'
; -- 必须加分号
with
t_tree as
(
    select CountryRegionCode from person.CountryRegion where Name like @s
)
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值,然后就进入了的递归过程,如下图所示。



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



[ WITH <common_table_expression> [ ,n ] ]
<common_table_expression>::=
         expression_name [ ( column_name [ ,n ] ) ]
    AS (
        CTE_query_definition1  --   定位点成员(也就是初始值或第一个结果集)
       union all
        CTE_query_definition2  --   递归成员
     )

   



with
district as
(
    --   获得第一个结果集,并更新最终结果集
    select * from t_tree where node_name= N'辽宁省'
    union all
    --   下面的select语句首先会根据从上一个查询结果集中获得的id值来查询parent_id        
    --   字段的值,然后district就会变当前的查询结果集,并继续执行下面的select 语句
    --   如果结果集不为null,则与最终的查询结果合并,同时用合并的结果更新最终的查
    --   询结果;否则停止执行。最后district的结果集就是最终结果集。
    select a.* from t_tree a, district b
               where a.parent_id = b.id
)
select * from district






with
district as
(
    select * from t_tree where node_name= N'辽宁省'
    union all
    select a.* from t_tree a, district b
               where a.parent_id = b.id
),
district1 as
(
    select a.* from district a where a.id in (select parent_id from district)   
)
select * from district1


  



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

    在定义和使用递归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 的递归成员中引用了远程服务器,那么将为每个远程表创建一个假脱机,这样就可以在本地反复访问这些表。

 

递归实例:

CREATE TABLE Dept(
    id int PRIMARY KEY,
    parent_id int,
    name nvarchar(20))
INSERT Dept
SELECT 0, 0, N'<全部>' UNION ALL
SELECT 1, 0, N'财务部' UNION ALL
SELECT 2, 0, N'行政部' UNION ALL
SELECT 3, 0, N'业务部' UNION ALL
SELECT 4, 0, N'业务部' UNION ALL
SELECT 5, 4, N'销售部' UNION ALL
SELECT 6, 4, N'MIS' UNION ALL
SELECT 7, 6, N'UI' UNION ALL
SELECT 8, 6, N'软件开发' UNION ALL
SELECT 9, 8, N'内部开发'
GO


-- 查询指定部门下面的所有部门
DECLARE @Dept_name nvarchar(20)
SET @Dept_name = N'MIS'
;WITH
DEPTS AS(
    -- 定位点成员
    SELECT * FROM Dept
    WHERE name = @Dept_name
    UNION ALL
    -- 递归成员, 通过引用CTE自身与Dept基表JOIN实现递归
    SELECT A.*
    FROM Dept A, DEPTS B
    WHERE A.parent_id = B.id
)
SELECT * FROM DEPTS
GO

-- 删除演示环境
--DROP TABLE Dept

分享到:
评论

相关推荐

    巧用with as 对sql语句多次利用

    对于需重复查询的sql语句,可巧用with as 对sql语句进行精简,提高查询效率

    sqlserver 存储过程With Encryption加密的解密

    在SQL Server中,为了保护存储过程中的敏感代码或逻辑,可以使用`WITH ENCRYPTION`选项对存储过程进行加密处理。这可以有效防止未经授权的用户查看存储过程的源代码。然而,在某些情况下,出于维护、调试或者迁移的...

    sql中with as的应用整理

    下面我们将详细介绍`WITH AS`的使用场景、语法结构及其在实际应用中的几个示例。 #### 基本概念与语法 `WITH AS`定义了一个名为CTE(Common Table Expression)的构造,其基本语法如下: ```sql WITH CTE_Name ...

    SQL Server中with as使用介绍

    在SQL Server中,`WITH AS` 语句,也称为公用表表达式(Common Table Expression,简称CTE),是一种强大的工具,它允许我们为复杂的查询创建临时的结果集,这个结果集可以在同一个查询中多次引用。这极大地提高了...

    Data Science with SQL Server

    R is one of the most popular, powerful data analytics languages and environments in use ...concepts are explained in case you are not familiar with SQL Server, as is often the case with data scientists.

    Professional SQL Server 2012 Analysis Services with MDX and DAX part2

    This title serves as an authoritative guide to Microsofts new "SQL Server 2012 Analysis Services" BI product and is written by key members of the Microsoft Analysis Services product development team....

    sql server 中如何增加递增的序号列【实用】

    sql server 中如何增加递增的序号列 sql server 是一种功能强大且广泛应用的关系...在 sql server 中增加递增的序号列可以使用多种方法,每种方法都有其特点和优缺。选择哪种方法取决于具体的业务需求和数据特点。

    SqlServer调用C#DLL的方法

    通过以上步骤,我们成功地在SqlServer环境中配置并使用了一个由C#编写的DLL,实现了加解密功能。这种方式不仅可以提升应用程序的性能,还能更好地整合业务逻辑,是现代数据库应用的一个重要组成部分。

    SQL Server中存储过程比直接运行SQL语句慢的原因

    在 SQL Server 中,存储过程比直接运行 SQL 语句慢的原因是 Parameter sniffing 问题。Parameter sniffing 是指 SQL Server 在执行存储过程时,使用参数的统计信息来优化执行计划,但这种优化方式有时可能会导致执行...

    如何使用SQL Server数据库中查询累计值

    本文将介绍使用 SQL Server 数据库中查询累计值的方法。 一、创建表格 首先,我们需要创建一个表格来存储银行账户的交易记录。我们可以使用以下 SQL 语句创建一个名为 BankAccount 的表格: ```sql CREATE TABLE ...

    SQLServer中Partition By及row_number 函数使用详解

    在SQL Server中,`PARTITION BY` 和 `ROW_NUMBER()` 是两种非常重要的分析函数,它们在处理大数据集时尤其有用。本文将详细讲解这两个函数的使用方法及其在实际场景中的应用。 `PARTITION BY` 关键字是分析函数的一...

    mssql sqlserver 使用sql脚本实现相邻两条数据相减.docx

    在本文中,我们将讨论如何使用 SQL 脚本在 MSSQL SQL Server 中实现相邻两条数据相减的方法。 创建表和插入数据 首先,我们需要创建一个名为 XXX 的表,并插入一些示例数据。以下是创建表和插入数据的 SQL 语句: ...

    Beginning T-SQL with Microsoft SQL Server 2005 and 2008

    The SQL Server client tools must be installed on your desktop computer and the SQL Server relational database server must be installed on either your desktop computer or on a remote server with ...

    SQLSERVER使用说明

    在SQL Server中,数据库管理和数据查询是其核心功能。SQL(Structured Query Language)是一种用于管理关系数据库的标准语言,包括创建、更新、查询和删除数据。本使用说明将深入讲解SQL Server的基本语法和常用操作...

    ORACLE和SQL Server的语法区别

    本文主要介绍Oracle与SQL Server在SQL语言层面的异同之处,重点在于Transact-SQL(T-SQL)与PL/SQL之间的区别,并提供了一些迁移策略。对于希望将现有的Oracle数据库系统迁移至SQL Server平台的用户来说,这些知识尤...

    Oracle和SqlServer语法区别

    这些子句在SqlServer中不支持,但是可以使用其他方式实现相同的结果。 二、函数和过程 Oracle和SqlServer都支持函数和过程,但是它们之间存在一些差异。 Oracle支持PL/SQL函数和过程,而SqlServer支持Transact-...

    sqlserver sql语法大全

    ### SQLServer SQL语法大全 #### 创建数据库 在SQL Server中创建数据库是一项基本操作,通过`CREATE DATABASE`命令来实现。创建数据库时可以指定文件和日志文件的位置、初始大小及增长方式等参数。 **语法示例:**...

    SqlServer删除所有表数据语句

    下面我们将介绍如何使用一条 Sql Server 语句删除当前数据库中所有表的数据。 删除所有表数据 删除所有表数据的语句使用了存储过程来实现。首先,我们需要创建一个存储过程 `sp_DeleteAllData`,该过程将删除所有...

Global site tag (gtag.js) - Google Analytics