在两个日期时间范围之间每小时插入一行,包括持续时间(Oracle)

时间:2016-10-20 11:01:25

标签: oracle11g

我在date_range表中有两个日期时间记录“start_date”和“end_date”。

我想每小时间隔每小时插入一行,加上一个持续时间(以小时为单位)的列,以便我的结果返回如下:

e.g。 start_date = 2016/09/01 21:12:00和end_date = 2016/09/02 01:30:00

Date         Hour    Duration
2016/09/01   21      0.8
2016/09/01   22      1
2016/09/01   23      1
2016/09/02   00      1
2016/09/02   01      0.5

3 个答案:

答案 0 :(得分:0)

You can do it with procedure like this:

first I prepare your data;

create table date_range 
(
start_date date,
end_date date
);

create table result_table
(
date_ varchar2(10),
hour_ varchar2(2),
duration number
);


insert into date_range    
select to_date('2016/09/01 21:12:00','YYYY/MM/DD hh24:mi:ss'),to_date('2016/09/02 01:30:00','YYYY/MM/DD hh24:mi:ss') from dual ;

commit;

second I create your procedure;

CREATE OR REPLACE procedure get_dates 
AS 
  rn number:=0;
  temp varchar2(10); 
BEGIN

 select 
ceil(24* ( end_date-start_date ))-1
into rn 
 from
date_range;


FOR  i IN 0..rn 
   LOOP

    temp:=i||'/24';

    EXECUTE IMMEDIATE '
       insert into result_table
         select 
        to_char(start_date+'||temp||',''yyyy/mm/dd'')date_,
        to_char(start_date+'||temp||',''hh24'')hour_,
        case 
          when '||i||'=0 
           then 1-to_number(to_char(start_date+'||temp||',''mi''))/60
          when '||i||'='||rn||' 
           then 1-to_number(to_char(end_date+'||temp||',''mi''))/60
           else 1
        end duration
        from
        date_range ';

        commit;

   END LOOP;



END get_dates;

thirdly I execute procedure ;

begin
get_dates();
end;

finally I see the expected result ;

select   
* from
result_table;



DATE_        HOUR_  DURATION
2016/09/01    21    0,8
2016/09/01    22    1
2016/09/01    23    1
2016/09/02    00    1
2016/09/02    01    0,5

答案 1 :(得分:0)

select case when lvl=mn then (trunc(frst)+1)- frst
            when lvl=mx then lst-trunc(lst) 
       else 1
       end ,a.*
        from  
(
select frst,lst,sonuc,lvl,first_value(lvl)over() mn,last_value(lvl) over() mx
   from  
(select to_date('2016/09/01 21:12:00','YYYY/MM/DD hh24:mi:ss') frst,
        to_date('2016/09/02 01:30:00','YYYY/MM/DD hh24:mi:ss') lst,
       (to_date('2016/09/02 01:30:00','YYYY/MM/DD hh24:mi:ss') - to_date('2016/09/01 21:12:00','YYYY/MM/DD hh24:mi:ss'))*24 sonuc from dual) a  
, 
( select level lvl 
   from dual
connect by level <=
(select
        case when (to_date('2016/09/02 01:30:00','YYYY/MM/DD hh24:mi:ss') - to_date('2016/09/01 21:12:00','YYYY/MM/DD hh24:mi:ss'))*24> 
        trunc((to_date('2016/09/02 01:30:00','YYYY/MM/DD hh24:mi:ss') - to_date('2016/09/01 21:12:00','YYYY/MM/DD hh24:mi:ss'))*24)
        then trunc((to_date('2016/09/02 01:30:00','YYYY/MM/DD hh24:mi:ss') - to_date('2016/09/01 21:12:00','YYYY/MM/DD hh24:mi:ss'))*24)+1 
        else trunc((to_date('2016/09/02 01:30:00','YYYY/MM/DD hh24:mi:ss') - to_date('2016/09/01 21:12:00','YYYY/MM/DD hh24:mi:ss'))*24)
        end 
   from dual)
) b
)a  

答案 2 :(得分:0)

这是一个简单的SQL解决方案(最好尽可能避免使用PL / SQL而不是太复杂)。它使用自Oracle 11.1以来可用的递归因子子查询。我创建了几个&#34;行&#34;测试数据,以显示这可能同时对多对输入有效。请注意,第一个子查询不是解决方案的一部分 - 您将使用实际的表和列名称或任何输入源替换它(以及实际解决方案中对它的引用,这是查询的其余部分)。

还要注意&#34; date&#34;和&#34;小时&#34;是Oracle中的保留字,它们不应该用作列名(在输出中或其他任何地方)。我改为使用dthr

with
     date_range ( row_id, start_date, end_date ) as (
       select 101, to_date('2016/09/01 21:12:00', 'yyyy/mm/dd hh24:mi:ss'),
              to_date('2016/09/02 01:30:00', 'yyyy/mm/dd hh24:mi:ss') from dual union all
       select 102, to_date('2016/09/02 21:00:00', 'yyyy/mm/dd hh24:mi:ss'),
              to_date('2016/09/02 22:00:00', 'yyyy/mm/dd hh24:mi:ss') from dual union all
       select 103, to_date('2016/09/01 15:00:00', 'yyyy/mm/dd hh24:mi:ss'),
              to_date('2016/09/01 15:30:00', 'yyyy/mm/dd hh24:mi:ss') from dual union all
       select 104, to_date('2016/09/01 21:12:00', 'yyyy/mm/dd hh24:mi:ss'),
              to_date('2016/09/01 21:30:00', 'yyyy/mm/dd hh24:mi:ss') from dual
     ),
     rec ( row_id, from_time, to_time, end_date ) as (
       select  row_id, start_date, 
                least(end_date, trunc(start_date, 'hh') + 1/24), end_date
         from  date_range
       union all
       select  row_id, to_time, least(end_date, to_time + 1/24), end_date
         from  rec
         where end_date > from_time + 1/24
     )
select row_id, 
       to_char(from_time, 'yyyy/mm/dd') as dt,
       to_char(from_time, 'hh24') as hr, 
       round(24 * (to_time - from_time), 2) as duration
from   rec
order by row_id, from_time
;

<强>输出

    ROW_ID DT         HR   DURATION
---------- ---------- -- ----------
       101 2016/09/01 21         .8
       101 2016/09/01 22          1
       101 2016/09/01 23          1
       101 2016/09/02 00          1
       101 2016/09/02 01         .5
       102 2016/09/02 21          1
       103 2016/09/01 15         .5
       104 2016/09/01 21         .3

 8 rows selected