如何生成从开始到今天每30天一次的列表周期

时间:2017-02-07 13:37:11

标签: sql oracle plsql

我为一家公司工作,该公司要求我生成自项目开始至今为止每30天的一段时间列表。 示例:项目“A”的开始日期为01 / DEC / 2016,项目“B”的开始日期为2016年2月5日。 今天是07 / FEB / 2017。

输出应该是这样的:

**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件或更多件物品。

提前谢谢。

3 个答案:

答案 0 :(得分:4)

var test = function(element) {

    var tdCaller = $(element);
    var parentRow = tdCaller.parent();
    var parentBody = parentRow.parent();

    // my add
    $('tr.mytr.clicked').removeClass('clicked')
    parentRow.addClass('clicked');
  }
.MarkSelectedRow {
  background-color: cornflowerblue;
}

/* my add */
.table-hover tbody tr.mytr.clicked {
  background-color: white;
}

答案 1 :(得分:2)

在Oracle 11g及更低版本中,您可以使用嵌套表来实现此目的:

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;

如果您使用的是Oracle 12c +,则可以使用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;

以上查询都会产生:

+------------+-----------+-----------+
| 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
)

答案 2 :(得分:1)

Numbers CTE,一些数学和案例陈述

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