3

I'm using Postgresql 9.2 and I need to sum quantitys from buttom up with initial value of 100, however if I encounter a row with name X i need to restart the SUM from the value of qty in this row.

for example:

itemorder   name    qty
  1          A       -20
  2          A2       350
  3          X        40
  4          A        50
  5          A        -10
  6          A2       10

Should generate:

itemorder   name    qty       modifyed_sum
  1          A       -20          20       / 40 + (-20)
  2          A2       350         40       / not A
  3          X        40           40       / encounter X, restart sum with X qty.
  4          A        50          140       / 90 + 50
  5          A        -10         90        / 100 +  (-10)
  6          A2       10          100       / not A

I used this query to do the Sum:

SELECT 100 + Sum(CASE WHEN name = 'a' THEN qty ELSE 0 END)OVER(ORDER BY itemorder DESC) as modifyed_sum,
       qty,
       name,
       itemorder
FROM   Table_Name
ORDER  BY itemorder ASC

How can I modify it to handel the X case? (There can be more than one X)

java
  • 1,124
  • 2
  • 14
  • 33
  • I dont understand that `SUM()`algorithm, can you elaborate? – Juan Carlos Oropeza Jan 10 '16 at 04:57
  • I already explained near each row how it works. Note the sum is BUTTOM UP not TOP DOWN. This is my earlier question: http://stackoverflow.com/questions/34487883/how-to-calculate-a-cumulative-sum-from-the-bottom-up The current question extend it. – java Jan 10 '16 at 05:05

2 Answers2

3

You need a RECURSIVE query

SQL FIDDLE DEMO

WITH RECURSIVE t("itemorder", "name", "qty", "modifyed_sum", "level") AS (
    SELECT "itemorder", 
           "name", 
           "qty", 
           100 +  CASE 
                     WHEN "name" = 'A' THEN "qty"
                     ELSE 0
                  END as "modifyed_sum",
           "itemorder" as "level"
     FROM myTable
     WHERE "itemorder" = (SELECT max("itemorder") FROM myTable)
     UNION ALL
     SELECT myTable."itemorder", 
            myTable."name", 
            myTable."qty", 
            CASE 
                WHEN myTable."name" = 'A' THEN t."modifyed_sum" + myTable."qty"
                WHEN myTable."name" = 'X' THEN myTable."qty"
                ELSE t."modifyed_sum"          
            END as "modifyed_sum",
           myTable."itemorder" as "level"
     FROM myTable 
     JOIN t
       ON myTable."itemorder" + 1 = t."itemorder" 
      AND myTable."itemorder" = t."level" - 1
     WHERE  myTable."itemorder" > 0        
)
SELECT *
FROM t
ORDER BY "itemorder"

OUTPUT

| itemorder | name | qty | modifyed_sum | level |
|-----------|------|-----|--------------|-------|
|         1 |    A | -20 |           20 |     1 |
|         2 |   A2 | 350 |           40 |     2 |
|         3 |    X |  40 |           40 |     3 |
|         4 |    A |  50 |          140 |     4 |
|         5 |    A | -10 |           90 |     5 |
|         6 |   A2 |  10 |          100 |     6 |
Juan Carlos Oropeza
  • 47,252
  • 12
  • 78
  • 118
  • Isn't there a RESET command in window function? Its there in http://stackoverflow.com/questions/13819710/how-to-calculate-moving-sum-with-reset-based-on-condition-in-teradata-sql – Marlon Abeykoon Jan 10 '16 at 05:33
  • is it really doable with RESET? – java Jan 10 '16 at 06:59
  • @MarlonAbeykoon: that linked question is about Teradata, not Postgres. Postgres does not have a `RESET` option for the window definition –  Jan 10 '16 at 08:56
1

To reset a Windowed Aggregate based on a condition you need to add another level to define the different groups of rows:

with cte as
 (
   select itemorder, name, qty,
      case -- start qty for each group
         when itemorder = max(itemorder) over () then 100 -- newest row
         when name = 'X' then qty
         else 0
      end 
           -- only sum 'A' rows
      + case when name = 'A' then qty else 0 end as new_qty, 

           -- grp value increases only when name = 'X'
           -- this assigns the same value to each row in a group
           -- will be used in the next step to PARTITION BY
      sum(case when name = 'X' then 1 else 0 end) 
      over (order by itemorder desc
            rows unbounded preceding) as grp
   from myTable
 )

select itemorder, name, qty,
   sum(new_qty)
   over (partition by grp
         order by itemorder desc
         rows unbounded preceding ) as modified_sum
from cte
order by item order

See fiddle

dnoeth
  • 59,503
  • 4
  • 39
  • 56