zoukankan      html  css  js  c++  java
  • Sql Server2005 TransactSQL 新兵器学习总结之公用表表达式(CTE)

    公用表表达式是Sql Server2005新增加的一个非常好用的功能。

    公用表表达式 (CTE) 可以认为是在单个 SELECT、INSERT、UPDATE、DELETE 或 CREATE VIEW 语句的执行范围内定义的临时结果集。
    CTE 与派生表类似,具体表现在不存储为对象,并且只在查询期间有效。
    与派生表的不同之处在于,CTE 可自引用,还可在同一查询中引用多次。

    CTE可用于:
    1.创建递归查询(我个人认为CTE最好用的地方)
    2.在同一语句中多次引用生成的表

    CTE优点:
    使用 CTE 可以获得提高可读性和轻松维护复杂查询的优点。
    查询可以分为单独块、简单块、逻辑生成块。之后,这些简单块可用于生成更复杂的临时 CTE,直到生成最终结果集。

    CTE可使用的范围:
    可以在用户定义的例程(如函数、存储过程、触发器或视图)中定义 CTE。

    下面看一个简单的CTE例题:
    把test表中salary最大的id记录保存在test_CTE中,再调用

    with test_CTE(id,salary)
    as
    (
        
    select id ,max(salary)
        
    from test
        
    group by id
    )
    select * from test_cte



    由上面例题可以看出:
    CTE 由表示 CTE 的表达式名称、可选列列表和定义 CET 的查询组成。
    定义 CTE 后,可以在 SELECT、INSERT、UPDATE 或 DELETE 语句中对其进行引用,就像引用表或视图一样。
    简单的说CTE可以替代临时表和表变量的功能。

    我个人认为cte最好用的地方是创建递归查询,下面演示一下这功能:
    现有一数据结构如下:
    QQ截图未命名1.bmp
    这些数据存放在表Co_ItemNameSet中,表结构和部分数据如下:
    ItemId      ParentItemId ItemName
    2           0            管理费用
    3           0            销售费用
    4           0            财务费用
    5           0            生产成本
    35          5            材料
    36          5            人工
    37          5            制造费用
    38          35           原材料
    39          35           主要材料
    40          35           间辅材料
    41          36           工资
    42          36           福利
    43          36           年奖金

    现在需求是:我想查询ItemId=2,也就是管理费用和其下属所有节点的信息
    通过CTE可以很简单达到需求要的数据
    为了体现CTE的方便性,我特意也写了一个sql2000版本的解决方法,先看看sql2000是怎么解决这个问题的


    --sql2000版本
    DECLARE @i INT
    SELECT @i=2;

    /*
    使用临时表作为堆栈来跟踪所有正在处理中的项目(已经开始但尚未结束)。
    某个项目一旦处理完毕,将被从堆栈中删除。
    当发现新的项目时,这些项目将被添加到堆栈中。
    */
    CREATE TABLE #tem(
    [ItemId] [INT]  NOT NULL,
    [level] INT
    );

    /*
    存放结果
    */
    CREATE TABLE #list(
    [ItemId] [INT]  NOT NULL,
    [ParentItemId] [INT] NOT NULL DEFAULT ((0)),
    [ItemName] [nvarchar](100NOT NULL DEFAULT (''),
    [level] INT
    );

    INSERT INTO #tem([ItemId],[level])
    SELECT  ItemId, 1
    FROM  Co_ItemNameSet
    WHERE itemid=@i  

    INSERT INTO #list([ItemId],[ParentItemId],[ItemName],[level])
    SELECT  ItemId, ParentItemId, ItemName ,1
    FROM  Co_ItemNameSet
    WHERE itemid=@i  

    DECLARE @level INT
    SELECT @level=1

    DECLARE @current INT
    SELECT @current=0

    /*
    当 @level 大于 0 时,执行以下步骤: 
    1.如果当前级别 (@level) 的堆栈中有项目,就选择其中一个,并称之为 @current。
    2.从堆栈中删除该项目以免重复处理它,然后将其所有子项目添加到堆栈的下一级 (@level + 1) 中。  
    3.如果有子项目 (IF @@ROWCOUNT > 0),则下降一级处理它们 (@level = @level + 1);否则,继续在当前级别上处理。
    4.最后,如果在当前级别的堆栈中没有待处理的项目,则返回到上一级,看上一级是否有待处理的项目 (@level = @level - 1)。当再没有上一级时,则完毕。 
    */
    WHILE(@level>0)
    BEGIN
        
    SELECT @current=ItemId
        
    FROM #tem
        
    WHERE [level]=@level

        
    IF @@ROWCOUNT>0
        
    BEGIN
            
    --从堆栈中删除该项目以免重复处理它
            DELETE FROM #tem
            
    WHERE [level]=@level and ItemId=@current

            
    --将其所有子项目添加到堆栈的下一级 (@level + 1) 中。  
            INSERT INTO #tem([ItemId],[level])
            
    SELECT [ItemId],@level+1
            
    FROM Co_ItemNameSet
            
    WHERE ParentItemId=@current
        
            
    --将其所有子项目添加
            INSERT INTO #list([ItemId],[ParentItemId],[ItemName],[level])
            
    SELECT [ItemId],[ParentItemId],[ItemName] ,@level+1
            
    FROM Co_ItemNameSet
            
    WHERE ParentItemId=@current
            
            
    IF @@rowcount>0
            
    BEGIN
                
    SELECT @level=@level+1
            
    END
        
    END
        
    ELSE
        
    BEGIN
            
    SELECT @level=@level-1
        
    END
    END

    --显示结果
    SELECT * FROM #list

    DROP TABLE #tem
    DROP TABLE #list
    go



    结果如下:
    ItemId ParentItemId ItemName level
    2     0             管理费用     1
    52     2             汽车费用     2
    55     2             招聘费     2
    56     2             排污费     2
    53     52             燃料          3
    54     52             轮胎          3

    大家看到sql2000解决这个问题比较麻烦,要实现这需求编写的代码比较多,比较复杂
    现在好了,在sql2005中通过CTE的递归特点可以2步就实现.
    得到同样的结果,sql2005的CTE代码简单了许多.这就是CTE支持递归查询的魅力。
    请看下面的代码:


    --sql2005版本
    DECLARE @i INT
    SELECT @i=2;

    WITH Co_ItemNameSet_CTE(ItemId, ParentItemId, ItemName,Level)
    AS
    (
        
    SELECT  ItemId, ParentItemId, ItemName ,1 AS [Level]
        
    FROM  Co_ItemNameSet
        
    WHERE itemid=@i  
        
    UNION ALL
        
    SELECT  c.ItemId, c.ParentItemId, c.ItemName ,[Level] + 1
        
    FROM  Co_ItemNameSet c  INNER JOIN Co_ItemNameSet_CTE ct
        
    ON c.ParentItemId=ct.ItemId
    )

    SELECT * FROM Co_ItemNameSet_CTE
    go

  • 相关阅读:
    121. Best Time to Buy and Sell Stock
    70. Climbing Stairs
    647. Palindromic Substrings
    609. Find Duplicate File in System
    583. Delete Operation for Two Strings
    556 Next Greater Element III
    553. Optimal Division
    539. Minimum Time Difference
    537. Complex Number Multiplication
    227. Basic Calculator II
  • 原文地址:https://www.cnblogs.com/cpcpc/p/2123043.html
Copyright © 2011-2022 走看看