13

(月)A順に並べられた値で構成される次のデータがあります。MM

B列はGREATEST(current value of A + previous value of B, 0)、スプレッドシートのような方法で計算されます。

BSQL クエリを使用して計算するにはどうすればよいですか?

  • 分析関数を使用してみましたが、うまくいきませんでした。
  • モデル条項があることは知っています。同様の例を見つけましたが、どこから始めればよいかわかりません。

Oracle 10g を使用しているため、再帰クエリを使用できません。


これが私のテストデータです:

MM         | A      | B
-----------+--------+------
2012-01-01 |    800 |  800
2012-02-01 |   1900 | 2700
2012-03-01 |   1750 | 4450
2012-04-01 | -20000 |    0
2012-05-01 |    900 |  900
2012-06-01 |   3900 | 4800
2012-07-01 |  -2600 | 2200
2012-08-01 |  -2600 |    0
2012-09-01 |   2100 | 2100
2012-10-01 |  -2400 |    0
2012-11-01 |   1100 | 1100
2012-12-01 |   1300 | 2400

そして、ここに「テーブル定義」があります:

select t.* from (
  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
) t;
4

4 に答える 4

26

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
于 2012-09-03T10:16:40.577 に答える
7

ユーザー定義の集計関数を思いついた

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
于 2012-09-03T08:03:57.860 に答える
2
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を計算し、ゼロはその結果よりも大きいためです。abs関数を使用して計算で負の値を取り除くと、それを「修正」できます。

于 2012-09-03T07:41:42.220 に答える
2

質問の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
);
于 2017-10-03T14:14:42.667 に答える