CTE存储过程语法

时间:2016-04-22 18:47:10

标签: sql-server stored-procedures parameters common-table-expression

这可能是微不足道的,但我只是在学习CTE(感谢这里的帮助)。

我有一个用于确定总数的程序。

第一部分是总数是他们等级下面的位置的总和。所以我需要一种方法来检索记录(1)确定记录的级别(层次结构)和(2)返回所有记录的位置。有人问过并回答here

现在想从上面的答案中获取CTE表并在我的程序的第二部分中使用它(获得总数)

CREATE PROCEDURE [dbo].[GetProgramTotals]
    @programId nvarchar(10) = null,
    @owner int = null,
    @totalAmount money OUT,
    @usedAmount money OUT,
    @remainingAmount money OUT
AS
BEGIN
    WITH rCTE AS
    (
        SELECT 
            *, 0 AS Level 
        FROM Forecasting.dbo.Addressbook 
        WHERE Addressbook = @owner

        UNION ALL

        SELECT
            t.*, r.Level + 1 AS Level
        FROM Addressbook t
        INNER JOIN rCTE r ON t.ParentAddressbook = r.Addressbook
    )

    Select @totalAmount = (Select Sum(Amount) from dbo.Budget where 
                                (@programId IS NULL or (ProgramId = @programId)) and (@owner IS NULL or (BudgetOwner in (SELECT Addressbook from rCTE))))


    Select @usedAmount = (Select Sum(SubTotal) from dbo.OrderLine where 
                                (@programId IS NULL or (ProgramId = @programId) and (@owner IS NULL) or (Budget in (SELECT Addressbook from rCTE))))

    if (@totalAmount is null)
        set @totalAmount = 0

    if (@usedAmount is null)
        set @usedAmount = 0

    Set @remainingAmount = (@totalAmount - @usedAmount)
END

此过程的想法是基于层次结构中的用户位置动态计算单个(或所有)程序。

因此,区域经理总数将是所有地区和地区代表的总和。

更新:我根据下面的squillman(谢谢)评论对此进行了更新。

现在我有一个不同的问题。当我执行proc时 - 我得到了无效的对象名称rCTE'。

1 个答案:

答案 0 :(得分:2)

您无法在查询中使用SET。将其更改为SELECT,它可以解决您的语法错误。

CREATE PROCEDURE [dbo].[GetProgramTotals]
    @programId nvarchar(10) = null,
    @owner int = null,
    @totalAmount money OUT,
    @usedAmount money OUT,
    @remainingAmount money OUT
AS
BEGIN
    WITH rCTE AS(
    SELECT *, 0 AS Level FROM Forecasting.dbo.Addressbook WHERE Addressbook = @owner
    UNION ALL
    SELECT t.*, r.Level + 1 AS Level
    FROM Addressbook t
    INNER JOIN rCTE r ON t.ParentAddressbook = r.Addressbook)

    SELECT @totalAmount = (Select Sum(Amount) from dbo.Budget where 
                                (@programId IS NULL or (ProgramId = @programId)) and (@owner IS NULL or (BudgetOwner in (SELECT Addressbook from rCTE))))



    , @usedAmount = (Select Sum(SubTotal) from dbo.OrderLine where 
                                (@programId IS NULL or (ProgramId = @programId) and (@owner IS NULL) or (Budget in (SELECT Addressbook from rCTE))))

    if (@totalAmount is null)
        set @totalAmount = 0

    if (@usedAmount is null)
        set @usedAmount = 0

    Set @remainingAmount = (@totalAmount - @usedAmount)

END
一开始CTE可能有点令人困惑,但一旦有意义,它们真的很简单。对我而言,当我开始将它们视为另一种临时表语法时,它会点击它(专业提示:它们实际上不是概念上的)。所以基本上:

  1. 创建一个或多个“临时表”。这些是你的CTE表达式,可以有不止一个。
  2. 使用紧跟CTE后的声明中的一个或多个CTE表达式执行标准操作。
  3. 正如马丁在下面的评论中提到的那样,CTE仅限于下一个立即声明,并且在此之后不属于范围。

    所以,

    ;WITH cte1 AS
           (
            SELECT Col1 FROM Table1
           ),
          cte2 AS
           (
            SELECT Col1 FROM Table2
           )
    SELECT Col1 FROM cte1 //In scope here
    UNION
    SELECT Col1 FROM cte1; //Still in scope since we're still in the first statement
    
    SELECT Col1 FROM cte1; //Fails.  cte1 is now out of scope (as is cte2) 
    

    在您的情况下,您使用递归CTE来形成父/子层次结构,然后根据结果设置变量。在编辑之后,您的CTE语法非常接近,您只需要使用逗号将事物重新组合成一个语句。

    //Variable assignment example
    ;WITH cte1 AS
           (
            SELECT Col1 FROM Table1
           ),
          cte2 AS
           (
            SELECT Col1 FROM Table2
           )
    SELECT @var1 = (SELECT TOP 1 Col1 FROM cte1)
          ,@var2 = (SELECT TOP 1 Col1 FROM cte2) //You're missing the comma at the start of this line
    

    Select @usedAmount=...更改为, @usedAmount=...