2017-02-07 9 views
4

私は今までアイテムを開始してから30日ごとに期間のリストを生成するように要求している会社で働いています。 サンプル:アイテム 'A'の開始日は01/DEC/2016で、アイテム 'B'の開始日は05/Feb/2016です。 今日は07/FEB/2017です。最初から今日まで30日ごとにリスト期間を生成する方法

出力は次のようにする必要があります。ここ

**ITEM  | START  | END  | PERIOD_NO** 
---------------------------------------------- 
A   |01/12/2016 |30/12/2016 |0 
A   |31/12/2016 |29/01/2017 |1 
A   |30/01/2017 |28/02/2017 |2 
B   |05/07/2016 |03/08/2016 |0 
B   |04/08/2016 |02/09/2016 |1 
B   |03/09/2016 |02/10/2016 |2 
B   |03/10/2016 |01/11/2016 |3 
B   |02/11/2016 |01/12/2016 |4 
B   |02/12/2016 |31/12/2016 |5 
B   |01/01/2017 |30/01/2017 |6 
B   |31/01/2017 |01/03/2017 |7 

が私のコードです:

select 
      ITEM 
      , trunc(Start_Date+(level*30)-30) AS BEGIN 
      , CASE WHEN (Level-1) = 0 THEN trunc(Start_Date+(level*30) - 1) ELSE trunc(Start_Date+(level*30) -1) END AS END 
      , Level-1 AS Period   
    from 
    (
     Select 'A' ITEM 
       ,To_Date('05/07/2016', 'dd/MM/YYYY') Start_Date 
       , TRUNC(sysdate) END_Date        
       From Dual 
     /* UNION ALL 
     Select 
       'B' ITEM 
       , To_Date('01/02/2014', 'dd/MM/YYYY') Start_Date 
       , TRUNC(sysdate) END_Date        
       From Dual*/ 
    ) t 
    connect by level < (((END_DATE - START_DATE)/30) + 1) 

それだけで一つだけのアイテムのために働くように、私はそれを修正する方法をご助言を求めるしたいと思いますが2つ以上のアイテムで動作します。

ありがとうございます。

+0

使用しているOracleのバージョンはどれですか? – GurV

答えて

4
with t (item,start_date) as 
     (
         select 'A',date '2016-12-01' from dual 
      union all select 'B',date '2016-02-05' from dual 
     ) 

select   t.item 
       ,start_date + 30*(level-1)  as start_date 
       ,start_date + 30*level - 1  as end_date 
       ,level - 1      as period_no 

from   t   

connect by  item = prior item 
      and level <= 1 + (sysdate - start_date)/30 
      and prior sys_guid() is not null 

+------+---------------------+---------------------+-----------+ 
| ITEM | START_DATE   | END_DATE   | PERIOD_NO | 
+------+---------------------+---------------------+-----------+ 
| A | 2016-12-01 00:00:00 | 2016-12-30 00:00:00 | 0   | 
+------+---------------------+---------------------+-----------+ 
| A | 2016-12-31 00:00:00 | 2017-01-29 00:00:00 | 1   | 
+------+---------------------+---------------------+-----------+ 
| A | 2017-01-30 00:00:00 | 2017-02-28 00:00:00 | 2   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-02-05 00:00:00 | 2016-03-05 00:00:00 | 0   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-03-06 00:00:00 | 2016-04-04 00:00:00 | 1   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-04-05 00:00:00 | 2016-05-04 00:00:00 | 2   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-05-05 00:00:00 | 2016-06-03 00:00:00 | 3   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-06-04 00:00:00 | 2016-07-03 00:00:00 | 4   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-07-04 00:00:00 | 2016-08-02 00:00:00 | 5   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-08-03 00:00:00 | 2016-09-01 00:00:00 | 6   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-09-02 00:00:00 | 2016-10-01 00:00:00 | 7   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-10-02 00:00:00 | 2016-10-31 00:00:00 | 8   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-11-01 00:00:00 | 2016-11-30 00:00:00 | 9   | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-12-01 00:00:00 | 2016-12-30 00:00:00 | 10  | 
+------+---------------------+---------------------+-----------+ 
| B | 2016-12-31 00:00:00 | 2017-01-29 00:00:00 | 11  | 
+------+---------------------+---------------------+-----------+ 
| B | 2017-01-30 00:00:00 | 2017-02-28 00:00:00 | 12  | 
+------+---------------------+---------------------+-----------+ 
+0

パフォーマンスに関する2つの副次的な点: '以前のsys_guid()はnullではない'は同じ効果を持ち、「現在の」sys_guid()と比べると少し速いです。カットオフ条件は 'level <= 1 +(sysdate - start_date)/ 30'と書かれているので、RHSが一度だけ計算されます。それ以外の場合、算術演算は各行ごとに異なるため、すべての行について計算されます。 – mathguy

+0

@mathguy - うん。それはあなたが正しいと思う2です。ありがとう:-) –

+0

ありがとう、それは私をたくさん助けました。 –

1

番号CTE、数学のビットとcase文のOracle 11gでは

With numbers (NN) as 
(
select 0 as NN 
from dual 
union all 
select NN+1 
from numbers 
where NN < 100 
) 
, CTE as 
(
select item, 
     StartDate+(NN*30) as StartList, 
     row_number() over(partition by ITEM order by StartDate+(NN*30)) as PeriodNo 
from Numbers 
cross join MyTable 
where StartDate+(NN*30) < sysdate 
) 
select Item, 
     StartList, 
     case 
     when StartList +29 > sysdate then sysdate 
     else StartList +29 > sysdate 
     end as enddate, 
     PeriodNo 
from CTE 
3

以下は、この達成するために、ネストされたテーブルを使用することができます:あなたは、Oracle 12cの+を使用している場合

with t (dt) as (
    select to_date('01/12/2016','dd/mm/yyyy') from dual union all 
    select to_date('05/07/2016','dd/mm/yyyy') from dual 
) 
select 
    t.dt + (x.column_value - 1) * 30 start_date, 
    t.dt + x.column_value * 30 - 1 end_date, 
    x.column_value - 1 period_id 
from t cross join table(cast(
    multiset(
     select level 
     from dual 
     connect by t.dt + 30 * (level - 1) <= sysdate 
    ) as sys.odcinumberlist 
)) x; 

OUTER APPLYを使用すると、構文を大幅に簡略化できます。

with t (dt) as (
    select to_date('01/12/2016','dd/mm/yyyy') from dual union all 
    select to_date('05/07/2016','dd/mm/yyyy') from dual 
) 
select 
    t.dt + (x.n - 1) * 30 start_date, 
    t.dt + x.n * 30 - 1 end_date, 
    x.n - 1 period_id 
from t outer apply (
    select level n 
    from dual 
    connect by t.dt + 30 * (level - 1) <= sysdate 
) x; 

上記の両方ueryでは生成します

+------------+-----------+-----------+ 
| START_DATE | END_DATE | PERIOD_ID | 
+------------+-----------+-----------+ 
| 01-DEC-16 | 30-DEC-16 | 0   | 
+------------+-----------+-----------+ 
| 31-DEC-16 | 29-JAN-17 | 1   | 
+------------+-----------+-----------+ 
| 30-JAN-17 | 28-FEB-17 | 2   | 
+------------+-----------+-----------+ 
| 05-JUL-16 | 03-AUG-16 | 0   | 
+------------+-----------+-----------+ 
| 04-AUG-16 | 02-SEP-16 | 1   | 
+------------+-----------+-----------+ 
| 03-SEP-16 | 02-OCT-16 | 2   | 
+------------+-----------+-----------+ 
| 03-OCT-16 | 01-NOV-16 | 3   | 
+------------+-----------+-----------+ 
| 02-NOV-16 | 01-DEC-16 | 4   | 
+------------+-----------+-----------+ 
| 02-DEC-16 | 31-DEC-16 | 5   | 
+------------+-----------+-----------+ 
| 01-JAN-17 | 30-JAN-17 | 6   | 
+------------+-----------+-----------+ 
| 31-JAN-17 | 01-MAR-17 | 7   | 
+------------+-----------+-----------+ 

以下のクエリは、階層CONNECT BYを使用して、テーブルの各行の行生成します

select level n 
    from dual 
    connect by t.dt + 30 * (level - 1) <= sysdate 

を、以下CTEは、テストテーブルを構築することです:

with t (dt) as (
    select to_date('01/12/2016','dd/mm/yyyy') from dual union all 
    select to_date('05/07/2016','dd/mm/yyyy') from dual 
) 
関連する問題