简化,我有这样的表:
start_date end_date units
06/03/2014 14:00:00 06/03/2014 15:30:00 300
我想要做的是找出持续时间(end_date - start_date)。将其拆分为半小时间隔,然后在这些间隔之间平均分配单位。
在这种情况下,持续时间为90分钟,间隔3个半小时,即每个间隔100个单位。
由此我想在这些方面创造一些东西:
interval_date units
06/03/2014 14:00:00 100
06/03/2014 14:30:00 100
06/03/2014 15:00:00 100
我有这个工作,但它涉及多个子查询嵌套(可能可以清理一点使用更少)。它还涉及间隔的硬编码列,而不是一列来保持不同的间隔。例如,它看起来像这样:
id d0000 ... d1400 d1430 d1500 d1530 d1600 ... d2330
1 0 100 100 100 0 0 0
因此,在我想改变间隔持续时间(15分钟,1小时等)的情况下,它不是很灵活。
非常感谢任何想法或帮助。
答案 0 :(得分:2)
试试这个:
with t as (
select to_date('06/03/2014 14:00:00', 'dd/mm/yyyy HH24:mi:ss') start_time,
to_date('06/03/2014 15:10:00', 'dd/mm/yyyy HH24:mi:ss') end_time,
300 units
from dual
)
select start_time + (rownum - 1) / 48 start_interval,
units / ceil((end_time - start_time) * 48) units
from t
where start_date <> end_date
connect by level <= ceil((end_time - start_time) * 48);
START_INTERVAL UNITS
-------------- ----------
06.03.2014 14:00:00 100
06.03.2014 14:30:00 100
06.03.2014 15:00:00 100
更新:添加了WHERE
条件,以便在start_date
等于end_date
时避免除以零。
答案 1 :(得分:2)
如果您使用的是Oracle 11gR2,则可以使用递归CTE。
with x(start_date, end_date, units) as (
--select all the records and units per half an hour.
select start_date, end_date, units/((end_date - start_date)*48)
from myt
union all
--iteratively add 30 minutes to start_date till it stays less than end_date
select start_date + interval '30' minute, end_date, units
from x
where start_date + interval '30' minute < end_date
)
select start_date, units
from x
order by 1;
架构:
create table myt(
start_date date,
end_date date,
units number
);
insert into myt values(to_date('06/03/2014 14:00:00','mm/dd/yyyy hh24:mi:ss'),
to_date('06/03/2014 15:30:00','mm/dd/yyyy hh24:mi:ss'),
300
);
insert into myt values(to_date('06/04/2014 11:00:00','mm/dd/yyyy hh24:mi:ss'),
to_date('06/04/2014 15:30:00','mm/dd/yyyy hh24:mi:ss'),
300
);
输出:
START_DATE UNITS
-------------------------- ----------
03-JUN-2014 14:00:00 100
03-JUN-2014 14:30:00 100
03-JUN-2014 15:00:00 100
04-JUN-2014 11:00:00 33.3333333
04-JUN-2014 11:30:00 33.3333333
04-JUN-2014 12:00:00 33.3333333
04-JUN-2014 12:30:00 33.3333333
04-JUN-2014 13:00:00 33.3333333
04-JUN-2014 13:30:00 33.3333333
04-JUN-2014 14:00:00 33.3333333
04-JUN-2014 14:30:00 33.3333333
04-JUN-2014 15:00:00 33.3333333
答案 2 :(得分:1)
此解决方案涵盖任何行数(其他答案仅涵盖一行或多行存在性能问题)。
这就是我的尝试:
with yourTable as (select 1 id,
to_date('06/03/2014 14:00:00','dd/mm/yyyy hh24:mi:ss') start_date,
to_date('06/03/2014 15:30:00','dd/mm/yyyy hh24:mi:ss') end_date,
300 units
from dual
union all
select 2 id,
to_date('05/03/2014 12:00:00','dd/mm/yyyy hh24:mi:ss') start_date,
to_date('06/03/2014 23:30:15','dd/mm/yyyy hh24:mi:ss') end_date,
1000 units
from dual),
setting as (select 1/24/2 /*every half an hour*/ interval from dual),
borders as (select /* +materialize */ min(start_date) - 1 mindate,max(end_date) + 1 maxdate from yourTable),
periods as (select /* +materialize */
mindate + interval*level period_start,
mindate + interval*(level + 1) - 1/24/60/60 /* - 1 sec */ period_end
from borders,setting
connect by level <= (maxdate - mindate)/interval)
select id,
units,
units/count(*) over (partition by id)
from periods p, yourTable y
where y.start_date between p.period_start and p.period_end
or p.period_start between y.start_date and y.end_date;
其中
yourTable
应该只用您的表格替换setting
我用作设置期间的变量,使用1/24
表示小时或1/24/4
表示15分钟borders
是一个变量,其中包含句点的最大和最小日期periods
是所有需要时段的大部分内容yourTable
对于您的示例,它返回4行,因为15:30是逻辑上的新时段(15:29:59将作为上一时段的结束)。但我认为您可以自己处理这个问题:它需要有关您的业务逻辑的其他信息。
请注意,提示/* +materialize */
非常重要,因为如果没有它,Oracle Analyzer就会喜欢用connect by level
“打开”子查询,这会令人难以置信地表现不佳。