【问题标题】:TSQL GROUP BY in recursive CTE递归 CTE 中的 TSQL GROUP BY
【发布时间】:2012-12-07 00:58:05
【问题描述】:

是否有在循环 CTE 中使用 GROUP BY 的解决方法或有解决方法?

我需要对 CTE 表的结果集进行分组,并在具有相同 CTE 的另一个循环中使用它,但出现以下错误:

GROUP BY、HAVING 或聚合函数在 递归公用表表达式“cte”的递归部分。

这是查询:

WITH cte
    AS
    (
        SELECT
          id,
          dailyconsumption,
          stock/dailyconsumption as cutoff
        FROM items
        WHERE father IS NULL


        UNION ALL

        SELECT
          i.id,
          SUM(father.dailyconsumption*i.num),
          MAX(stock)/SUM(father.dailyconsumption*i.num)
        FROM cte father
        JOIN items i ON father.id=i.father
        group by i.id
    )

SELECT id, MIN(cutoff)
FROM cte
GROUP BY id

SQL-Fiddle(附样本数据)


编辑...这是逻辑问题

我有一组最终用户项 (father=NULL) 和由许多其他项组成的其他子项(字段父亲和字段 num 填充)。 我得到了最终用户项目的每日消耗量(我的 cte 以“WHERE 父亲为 NULL”开始),子项目的每日消耗量由 SUM(father.dailyconsumption *item.num) 计算。

WITH cte AS(
    SELECT
      id,
      dailyconsumption,
      stock/dailyconsumption as cutoff
    FROM items
    WHERE father IS NULL


    UNION ALL

    SELECT
      i.id,
      father.dailyconsumption*i.num
      0
    FROM cte father
    JOIN items i ON father.id=i.father
)

SELECT id, SUM(dailyconsumption)
FROM cte
GROUP BY id

http://sqlfiddle.com/#!3/f4f2a/95

通过这个有效查询,我将为所有项目(最终用户和子项目)填充所有日常消费。请注意,父子关系可以超过 1 级。

现在我需要计算截止日期(我的存货够用多少天)。 对于最终用途,它非常简单,并且已经在第一个 CTE 中计算出来:stock/dailyconsumption。 对于子项目,它有点复杂: subitem.stock/subitem.dailyconsumption + MIN(father.cutoff) 其中 MIN(father.cutoff) 是该子项的所有父亲的最小截止值。 这是因为我需要另一个分组。

我是否需要另一个 CTE 来循环相同的父子关系?

感谢您的关注,对不起我的英语。

【问题讨论】:

  • cte的递归部分分组是什么意思?我假设你有树,那么 item.id 在每次迭代中都是唯一的。
  • 期望的结果是什么(使用 SQL-Fiddle 中的数据)?
  • 显然,使用递归表时不能有任何 GROUP BY 或 AGGREGATE 函数...
  • 您能否给出小提琴数据的预期结果集?可能有很好的解决方案。

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


【解决方案1】:
;WITH cte AS
 (
  SELECT id, father, 
         dailyconsumption,
         (stock / dailyconsumption) AS cutoff,
         0 AS [Level] 
  FROM items
  WHERE father IS NULL
  UNION ALL
  SELECT i.id, i.father, 
         c.dailyconsumption * i.num,
         i.stock / (c.dailyconsumption * i.num),
         [Level] + 1
  FROM cte c JOIN items i ON c.id = i.father
  )
  SELECT c.id, c.dailyconsumption, c.cutoff AS subItemsCutoff, 
         MIN(ct.cutoff) OVER(PARTITION BY ct.[Level]) AS fatherCutoff,
         (c.cutoff  + ISNULL(MIN(ct.cutoff) OVER(PARTITION BY ct.[Level]), 0)) AS Cutoff
  FROM cte c LEFT JOIN cte ct ON c.father = ct.id

SQLFiddle上的演示

【讨论】:

    【解决方案2】:

    我建议改用变量表。声明表,然后将这些记录插入其中。您需要找到一种在第二次插入命令时循环遍历它的方法。我得到这个是为了让你开始:

    DECLARE @staging TABLE
    (
        id                  INT
        ,dailyconsumption   FLOAT
        ,cutoff             FLOAT
    )
    
    INSERT INTO @staging
        SELECT
          id,
          dailyconsumption,
          stock/dailyconsumption as cutoff
        FROM 
          items
        WHERE 
          father IS NULL
    
    INSERT INTO @staging
      SELECT
          i.id,
          SUM(father.dailyconsumption*i.num),
          MAX(stock)/SUM(father.dailyconsumption*i.num)
        FROM 
          @staging father
    
            JOIN items i 
            ON father.id=i.father
        group by 
          i.id
    
    SELECT 
      id
     ,MIN(cutoff)
    FROM 
      @staging
    GROUP BY 
      id
    

    【讨论】:

    • 这是为什么呢?插入后他可以从中选择。
    • 执行一级递归,但 CTE 将继续递归比这更深,直到树的最底部。我想你可以用一个表变量来做到这一点,但你还必须使用某种类型的 while 循环来一直递归到树下。
    • 我明白你在说什么。我认为在第二次插入命令时创建一个循环应该可以工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多