Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TSQL GROUP BY in recursive CTE

Is there a workaround to use GROUP BY inside a looping CTE or there is a workaround?

I need to group resultset of a CTE table and use it in another loop with the same CTE, but i get following error:

GROUP BY, HAVING, or aggregate functions are not allowed in the recursive part of a recursive common table expression 'cte'.

Here's the query:

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 (with sample data)


EDIT... this is the logical problem

I have a set of end-user items (father=NULL) and other sub-items made by a number of other items (field father and field num populated). I got the dailyconsumption just for the end-user items (I start my cte with "WHERE father IS NULL"), and sub-items's dailyconsumption are calculate by 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

With this valid query I'm going to have all dailyconsumption populated for all items (end-user and sub-items). Please mind that father-son relationship can be more than 1 level deep.

Now i need to calculate the cutoff (for how many days my stock is enought). For end-use it is very easy and already calculated in first CTE: stock/dailyconsumption. For sub-items it is a little more complicated: subitem.stock/subitem.dailyconsumption + MIN(father.cutoff) where MIN(father.cutoff) is the minimun cutoff from all fathers of this subitem. This is because i need another group by.

May I need another CTE to loop in the same father-son relationship?

Thank you for your attention and sorry for my English.

like image 818
Tobia Avatar asked Dec 19 '12 16:12

Tobia


People also ask

Can we use ORDER BY and group by in CTE?

You can not use ORDER BY inside the cte.

How recursive CTE works in SQL?

A recursive CTE references itself. It returns the result subset, then it repeatedly (recursively) references itself, and stops when it returns all the results. FROM cte_name; Again, at the beginning of your CTE is the WITH clause.

How does recursive CTE work?

A recursive CTE is a CTE that references itself. In doing so, the initial CTE is repeatedly executed, returning subsets of data, until the complete result is returned. Being able to reference itself is a unique feature and benefit.

What are the three main parts of the recursive CTE?

In general, a recursive CTE has three parts: An initial query that returns the base result set of the CTE. The initial query is called an anchor member. A recursive query that references the common table expression, therefore, it is called the recursive member.


1 Answers

;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

Demo on SQLFiddle

like image 187
Aleksandr Fedorenko Avatar answered Sep 28 '22 23:09

Aleksandr Fedorenko