【问题标题】:SQL Server CTE, How do I use the CTE table again for GROUP BYSQL Server CTE,如何将 CTE 表再次用于 GROUP BY
【发布时间】:2012-05-10 13:53:37
【问题描述】:

TableX 包含列和值

A   B   C   D   E   F   G   H   Level
---------------------------------------------------------------------
10  ABC 10  ABC XYZ 2   3   4   1
10  ABC 10  ABC XYZ 2   3   4   1
11  DEF 10  ABC XYZ 4   5   6   2
11  DEF 10  ABC XYZ 6   7   8   2
12  GHI 11  DEF XYZ 7   8   9   3
12  GHI 11  DEF XYZ 8   9   10  3
15  JKL 15  JKL ABC 9   10  11  1
15  JKL 15  JKL ABC 10  11  12  1
16  MNO 15  JKL ABC 13  14  15  2
16  MNO 15  JKL ABC 16  16  18  2

这里Level 不是表格中的一列,我只是为了清楚起见而添加,但CTE 会通过比较A、B 与C 和D 自动推断

对于级别 1,计算将是

Calc1 = (F + G - H)  
Calc2 = Calc1 + G + H  

2级及以上计算公式为

Calc1 = (上一级的Calc2) / 100的总和
Calc2 = Calc1 + G + H - 无论级别如何,此计算都没有变化

The calculation is cyclic - Level 2 的计算结果需要反馈到 Level 3 计算等等..

WITH Tiers (
     Level,
     A,
     B,
     C,
     D,
     E,
     Calc1,
     Calc2)
AS
SELECT 1 AS Level,
       A,
       B,
       C,
       D,
       E,
       F + G - H AS Calc1,
       (F + G - H) + G + H AS Calc2
 FROM TableX
WHERE A = C
  AND B = D
UNION ALL
SELECT tier.Level + 1 AS Level,
       A,
       B,
       C,
       D,
       E,
       /* How can I do this SUM (tier.Calc1) / 100 */
       /* How can I do this SUM (tier.Calc1) / 100 + G + H */
 FROM TableX tab
 INNER JOIN Tiers tier
    ON tab.C = tier.A
   AND tab.D = tier.B
   AND tab.E = tier.E
WHERE (A <> C
   OR B <> D)
)
SELECT *
  FROM Tiers

我试过了

SELECT tier.Level + 1 AS Level,
       A,
       B,
       C,
       D,
       E,
       sumCalc.calc1 / 100 Calc1
       sumCalc.calc1 / 100 + G + H Calc2
 FROM TableX tab
 INNER JOIN Tiers tier
    ON tab.C = tier.A
   AND tab.E = tier.B
 INNER JOIN (
      SELECT A, B, E, SUM(Calc1) Calc1
        FROM Tiers
      GROUP BY A, B, E) sumCalc /* SQL Server throws error that CTE table cannot be used here */
WHERE (A <> C
   OR B <> D)

我也希望使用表值的 OUTER APPLY 或其他东西..

【问题讨论】:

  • 尝试在那里使用层而不是层
  • 谢谢,打错了,已更新
  • 如果我理解你的问题,你不需要SUM()。您只需通过在 CTE 的后半部分添加这些值来累积这些值。 Level 不是SUM(),它是一系列添加的总和。
  • @user92546 抱歉没看懂你的回答,能告诉我怎么累积吗?我需要该值以及累积的 SUM。
  • @Humble - 您真的想要上一层的 所有 行的 SUM(),还是只想要一行?第一种情况是一个挑战,第二种情况就像Level:Tiers.Calc1 + ( G + H ) / 100一样处理。您的Calc1 定义中有错字吗?描述是指Calc2的总和,但代码使用'Calc1'。

标签: sql sql-server sql-server-2008 tsql common-table-expression


【解决方案1】:

编辑:在摆弄 CTE 之后,包括多个 CTE 一起滚动,它看起来并不乐观。

以下代码使用 CTE 分配级别并处理级别 1 计算并将结果保存在临时表中。然后一个循环按顺序处理每个附加级别。对于 SUM 的分组,我仍然(总体而言)有点困惑,但这应该很容易调整。

 declare @TableX as Table ( A Int, B VarChar(3), C Int, D VarChar(3), E VarChar(3), F Int, G Int, H Int )

 insert into @TableX ( A, B, C, D, E, F, G, H ) values
   ( 10, 'ABC', 10, 'ABC', 'XYZ', 2,  3,  4 ),
   ( 10, 'ABC', 10, 'ABC', 'XYZ', 2,  3,  4 ),
   ( 11, 'DEF', 10, 'ABC', 'XYZ', 4,  5,  6 ),
   ( 11, 'DEF', 10, 'ABC', 'XYZ', 6,  7,  8 ),
   ( 12, 'GHI', 11, 'DEF', 'XYZ', 7,  8,  9 ),
   ( 12, 'GHI', 11, 'DEF', 'XYZ', 8,  9,  10 ),
   ( 15, 'JKL', 15, 'JKL', 'ABC', 9,  10, 11 ),
   ( 15, 'JKL', 15, 'JKL', 'ABC', 10, 11, 12 ),
   ( 16, 'MNO', 15, 'JKL', 'ABC', 13, 14, 15 ),
   ( 16, 'MNO', 15, 'JKL', 'ABC', 16, 16, 18 )

-- Create a temporary table with levels assigned and the level 1 calculations performed.
; with Tiers ( Level, A, B, C, D, E, F, G, H, Calc1, Calc2 )
  as (
    -- Level 1.
    select 1, A, B, C, D, E, F, G, H, Cast( F + G - H as Float ), Cast( ( F + G - H ) + G + H as Float )
      from @TableX
      where A = C and B = D
    union all
    -- Level > 1.
    select T.Level + 1, TX.A, TX.B, TX.C, TX.D, TX.E, TX.F, TX.G, TX.H, NULL, NULL
      from @TableX as TX inner join
        Tiers as T on T.A = TX.C and T.B = TX.D and T.E = TX.E
      where TX.A <> TX.C or TX.B <> TX.D
    )
  select *
    into #Tiers
    from Tiers

-- Perform the additional calculations one level at a time.
declare @MinLevel as Int
select @MinLevel = Min( Level )
  from #Tiers
  where Calc1 is NULL

while @MinLevel is not NULL
  begin
  -- Note: The   SELECTs   for the sums may need extended   WHERE   clauses to implement the desired grouping.
  update #Tiers
    set Calc1 = ( select Sum( Calc2 ) from #Tiers where Level = @MinLevel - 1 ) / 100,
      Calc2 = ( select Sum( Calc2 ) from #Tiers where Level = @MinLevel - 1 ) / 100 + G + H
    where Level = @MinLevel
  select @MinLevel = Min( Level )
    from #Tiers
    where Calc1 is NULL
  end

-- Display the results.
select *
  from #Tiers
  order by Level

-- Houseclean.    
drop table #Tiers

为了完整起见,第一次尝试保留在下面:

也许解决方案是在构建 CTE 之后执行所有 Calc1/Calc2 计算:

 declare @TableX as Table ( A Int, B VarChar(3), C Int, D VarChar(3), E VarChar(3), F Int, G Int, H Int )

 insert into @TableX ( A, B, C, D, E, F, G, H ) values
   ( 10, 'ABC', 10, 'ABC', 'XYZ', 2,  3,  4 ),
   ( 10, 'ABC', 10, 'ABC', 'XYZ', 2,  3,  4 ),
   ( 11, 'DEF', 10, 'ABC', 'XYZ', 4,  5,  6 ),
   ( 11, 'DEF', 10, 'ABC', 'XYZ', 6,  7,  8 ),
   ( 12, 'GHI', 11, 'DEF', 'XYZ', 7,  8,  9 ),
   ( 12, 'GHI', 11, 'DEF', 'XYZ', 8,  9,  10 ),
   ( 15, 'JKL', 15, 'JKL', 'ABC', 9,  10, 11 ),
   ( 15, 'JKL', 15, 'JKL', 'ABC', 10, 11, 12 ),
   ( 16, 'MNO', 15, 'JKL', 'ABC', 13, 14, 15 ),
   ( 16, 'MNO', 15, 'JKL', 'ABC', 16, 16, 18 )

; with Tiers ( Level, A, B, C, D, E, F, G, H )
  as (
    -- Level 1.
    select 1, A, B, C, D, E, F, G, H
      from @TableX
      where A = C and B = D
    union all
    -- Level > 1.
    select T.Level + 1, TX.A, TX.B, TX.C, TX.D, TX.E, TX.F, TX.G, TX.H
      from @TableX as TX inner join
        Tiers as T on T.A = TX.C and T.B = TX.D and T.E = TX.E
      where TX.A <> TX.C or TX.B <> TX.D
    )
  select *,
    case Level
      when 1 then T.F + T.G - T.H
      else ( select Sum( F + G - H ) / 100. from Tiers where Level = T.Level - 1 ) end as Calc1,
    case Level
      when 1 then ( T.F + T.G - T.H ) + T.G + T.H
      else ( select Sum( F + G - H ) / 100. + T.G + T.H from Tiers where Level = T.Level - 1 ) end as Calc2
    from Tiers as T
    order by Level

请注意,我添加了一个小数点,以便计算结果返回实际值。

【讨论】:

  • 感谢@user92546 抽出宝贵时间。但是计算有一个问题,它们是循环的。如果看到,calculation1 是 SUM(上一级的 Calculation2),然后 Calculation2 再次引用我们刚刚计算的 Calculation1。在您的回答中,您正在执行两个单独的 SUM,在这种情况下,下一级的计算值将无法访问上一级的更新 Calc1 和 Calc2,因为我们正在计算而不是将值放回表中,因此下一级将参考他们。希望我能解释一下情况。
  • 我更新了原始问题并提到了循环性质。
  • 我错过了 SUM 查询中的 GROUP BY,在您的 SELECT CASE 中,我们还需要获取所有匹配的 A、B 和 E 记录并为 SUM 执行 GROUP BY A、B、E
  • 另一个问题,我们不能使用 GROUP BY,出现错误“递归公用表表达式的递归部分不允许使用 GROUP BY、HAVING 或聚合函数”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-07
  • 1970-01-01
  • 1970-01-01
  • 2017-08-29
  • 2017-10-16
相关资源
最近更新 更多