Oracle SQL Analytic query-рекурсивная электронная таблица-как работает total

у меня есть следующие данные в составе A значение, упорядоченных по

4 ответов


Так давайте развязать MODEL предложение (устройство, тайна которого превышена только его мощностью) по этой проблеме:

with data as (
  select date'2012-01-01' as mm,    800 as a from dual union all
  select date'2012-02-01' as mm,   1900 as a from dual union all
  select date'2012-03-01' as mm,   1750 as a from dual union all
  select date'2012-04-01' as mm, -20000 as a from dual union all
  select date'2012-05-01' as mm,    900 as a from dual union all
  select date'2012-06-01' as mm,   3900 as a from dual union all
  select date'2012-07-01' as mm,  -2600 as a from dual union all
  select date'2012-08-01' as mm,  -2600 as a from dual union all
  select date'2012-09-01' as mm,   2100 as a from dual union all
  select date'2012-10-01' as mm,  -2400 as a from dual union all
  select date'2012-11-01' as mm,   1100 as a from dual union all
  select date'2012-12-01' as mm,   1300 as a from dual
)
select mm, a, b
from (
  -- Add a dummy value for b, making it available to the MODEL clause
  select mm, a, 0 b
  from data
)
      -- Generate a ROW_NUMBER() dimension, in order to access rows by RN
model dimension by (row_number() over (order by mm) rn)
      -- Spreadsheet values / measures involved in calculations are mm, a, b
      measures (mm, a, b)
      -- A single rule will do. Any value of B should be calculated according to
      -- GREATEST([previous value of B] + [current value of A], 0)
      rules (
        b[any] = greatest(nvl(b[cv(rn) - 1], 0) + a[cv(rn)], 0)
      )

выше урожайность:

MM              A     B
01.01.2012    800   800
01.02.2012   1900  2700
01.03.2012   1750  4450
01.04.2012 -20000     0
01.05.2012    900   900
01.06.2012   3900  4800
01.07.2012  -2600  2200
01.08.2012  -2600     0
01.09.2012   2100  2100
01.10.2012  -2400     0
01.11.2012   1100  1100
01.12.2012   1300  2400

Я придумал пользовательскую агрегатную функцию

create or replace type tsum1 as object
  (
  total number,

  static function ODCIAggregateInitialize(nctx IN OUT tsum1 )
       return number,

  member function ODCIAggregateIterate(self IN OUT tsum1 ,
                                       value IN number )
       return number,

  member function ODCIAggregateTerminate(self IN tsum1,
                              retVal OUT  number,
                              flags IN number)
       return number,

  member function ODCIAggregateMerge(self IN OUT tsum1,
                          ctx2 IN tsum1)
       return number
)
/

create or replace type body tsum1
 is

 static function ODCIAggregateInitialize(nctx IN OUT tsum1)
 return number
 is
 begin
   nctx := tsum1(0);
   return ODCIConst.Success;
 end;

 member function ODCIAggregateIterate(self IN OUT tsum1,
                                    value IN number )
 return number
 is
 begin
   self.total := self.total + value;
     if (self.total < 0) then
       self.total := 0;
     end if;
   return ODCIConst.Success;
 end;

 member function ODCIAggregateTerminate(self IN tsum1,
                                        retVal OUT number,
                                        flags IN number)
 return number
 is
 begin
   retVal := self.total;
   return ODCIConst.Success;
 end;

 member function ODCIAggregateMerge(self IN OUT tsum1,
                                    ctx2 IN tsum1)
 return number
 is
 begin
   self.total := self.total + ctx2.total;
   return ODCIConst.Success;
 end;
 end;
 /

CREATE OR REPLACE FUNCTION sum1(input number)
RETURN number
PARALLEL_ENABLE AGGREGATE USING tsum1;
/

запрос

with T1 as(
   select date'2012-01-01' as mm, 800 as a from dual union all
   select date'2012-02-01' as mm, 1900 as a from dual union all
   select date'2012-03-01' as mm, 1750 as a from dual union all
   select date'2012-04-01' as mm, -20000 as a from dual union all
   select date'2012-05-01' as mm, 900 as a from dual union all
   select date'2012-06-01' as mm, 3900 as a from dual union all
   select date'2012-07-01' as mm, -2600 as a from dual union all
   select date'2012-08-01' as mm, -2600 as a from dual union all
   select date'2012-09-01' as mm, 2100 as a from dual union all
   select date'2012-10-01' as mm, -2400 as a from dual union all
   select date'2012-11-01' as mm, 1100 as a from dual union all
   select date'2012-12-01' as mm, 1300 as a from dual
 )

 select mm
      , a
      , sum1(a) over(order by mm) as b
  from t1

    Mm         a      b
 ----------------------------  
 01.01.2012    800    800  
 01.02.2012    1900   2700  
 01.03.2012    1750   4450  
 01.04.2012   -20000  0  
 01.05.2012    900    900  
 01.06.2012    3900   4800  
 01.07.2012   -2600   2200  
 01.08.2012   -2600   0  
 01.09.2012    2100   2100  
 01.10.2012   -2400   0  
 01.11.2012    1100   1100  
 01.12.2012    1300   2400

with sample_data as (
  select date'2012-01-01' as mm, 800 as a from dual union all
  select date'2012-02-01' as mm, 1900 as a from dual union all
  select date'2012-03-01' as mm, 1750 as a from dual union all
  select date'2012-04-01' as mm, -20000 as a from dual union all
  select date'2012-05-01' as mm, 900 as a from dual union all
  select date'2012-06-01' as mm, 3900 as a from dual union all
  select date'2012-07-01' as mm, -2600 as a from dual union all
  select date'2012-08-01' as mm, -2600 as a from dual union all
  select date'2012-09-01' as mm, 2100 as a from dual union all
  select date'2012-10-01' as mm, -2400 as a from dual union all
  select date'2012-11-01' as mm, 1100 as a from dual union all
  select date'2012-12-01' as mm, 1300 as a from dual
) 
select mm, 
       a, 
       greatest(nvl(a,0) + lag(a,1,0) over (order by mm), 0) as b
from sample_data;

тем не менее не произвести эту строку:

2012-05-01 |    900 |  900

потому что он вычисляет 900 - 20000 в эту строку, и ноль больше, чем результат. Вы можете "исправить" это, если используете


Извините, если это не по теме, учитывая версию вопроса Oracle, но теперь мы можем использовать предложение SQL: 2016 MATCH_RECOGNIZE:

select * from t
match_recognize(
  order by mm
  measures case classifier() when 'POS' then sum(a) else 0 end as b
  all rows per match
  pattern (pos* neg{0,1})
  define pos as sum(a) > 0
);