Home > Enterprise >  ORACLE SQL : SUM in first row with condition
ORACLE SQL : SUM in first row with condition

Time:07-22

I'm struggling to obtain the following results in a query

Here is my table

Line_num Line_typ Cost
1000 6 0
2000 7 5000
3000 7 3000
4000 7 2000
5000 6 0
6000 9 3000
7000 7 2000
8000 1 2000

What I want as result is this

Line_num Line_typ Cost
1000 6 10000 (0 5000 3000 2000)
5000 6 5000 (0 3000 2000)
8000 1 2000

Basically to display only rows with line_typ in (6,1) but sum the column costs of all other lines in between.

Thank you for your ideas and help !!

Ivan

CodePudding user response:

The reason Tim Biegeleisen's answer is excellent, but needs some intermediate products and oracle features, can be seen when one uses minimal SQL:

  • Select line_typ in (1, 6), order by line_num.
  • Sum the cost for second run over the table with larger line_num n, but
  • where (third run) there is no line_typ in (1, 6) before n.

Basic complexity O(N³), with indices a bit less.

So:

select h.line_num, h.line_typ,
 (select sum(cost)
  from tbl g
  where g.line_num >= h.line_num
  and not exists (select *
                  from tbl
                  where line_num > h.line_num
                  and line_typ in (1, 6)
                  and line_num <= g.line_num)
 ) as cost
from tbl h
where h.line_typ in (1, 6)
order by h.line_num

(Given for non-oracle searchers.)

Here is a link to a working screen capture from demo link below

Demo

CodePudding user response:

From Oracle 12, you can use MATCH_RECOGNIZE to perform row-by-row pattern matching:

SELECT *
FROM   table_name
MATCH_RECOGNIZE(
  ORDER BY line_num
  MEASURES
    FIRST(line_num) AS line_num,
    FIRST(line_typ) AS line_typ,
    SUM(cost)      AS total_cost
  PATTERN (match_type other_type*)
  DEFINE
    match_type AS line_typ IN (6, 1),
    other_type AS line_typ NOT IN (6, 1)
)

Which, for the sample data:

CREATE TABLE table_name (line_num, line_typ, cost) AS
  SELECT 1000, 6,    0 FROM DUAL UNION ALL
  SELECT 2000, 7, 5000 FROM DUAL UNION ALL
  SELECT 3000, 7, 3000 FROM DUAL UNION ALL
  SELECT 4000, 7, 2000 FROM DUAL UNION ALL
  SELECT 5000, 6,    0 FROM DUAL UNION ALL
  SELECT 6000, 9, 3000 FROM DUAL UNION ALL
  SELECT 7000, 7, 2000 FROM DUAL UNION ALL
  SELECT 8000, 1, 2000 FROM DUAL;

Outputs:

LINE_NUM LINE_TYP TOTAL_COST
1000 6 10000
5000 6 5000
8000 1 2000

db<>fiddle here

  • Related