首先,您的 CASE 陈述使事情变得过于复杂。您可以改用数学,更具体地说,Oracle 在进行日期算术时会返回一个数字。此外,转换为日期可确保您正确地进行算术运算。在这样的大型 CASE 语句中打错字很容易,这意味着一切都会出错。该解决方案将处理一天中的任何时间,而不仅仅是特定的半小时。
如果我按如下方式设置表格:
create table times ( starttime varchar2(5), endtime varchar2(5) );
insert into times values ( '00:30','07:30');
insert into times values ( '16:00','19:00');
insert into times values ( '19:00','20:00');
以下查询将为您提供大部分内容:
SQL> select starttime
, endtime
, trunc( ( to_date(starttime,'hh24:mi')
- trunc(sysdate) ) * 48) + 1 as starttime_x
, trunc( ( to_date(endtime,'hh24:mi')
- trunc(sysdate) ) * 48) + 1 as endtime_x
from times;
START ENDTI STARTTIME_X ENDTIME_X
----- ----- ----------- ----------
00:30 07:30 1 16
16:00 19:00 33 39
19:00 20:00 39 41
然后,您需要添加范围内所有值的逗号分隔列表。如果可能的话,我会避免这种情况……它对你没有多大用处,只能用于展示。但是,这些数据隐含在和之间的差异中starttime_x
,endtime_x
所以我看不出它带来了什么好处。
但是,假设您必须这样做,您将必须生成开始范围和结束范围之间的行,然后聚合它们。
SQL> with the_times as (
select starttime
, endtime
, trunc( ( to_date(starttime,'hh24:mi')
- trunc(sysdate) ) * 48) + 1 as starttime_x
, trunc( ( to_date(endtime,'hh24:mi')
- trunc(sysdate) ) * 48) + 1 as endtime_x
from times
)
, all_times as (
select level as t
from dual
connect by level <= 48
)
select starttime, endtime
, 'x' || starttime_x as starttime_x, 'x' || endtime_x as endtime_x
, listagg('x' || t, ', ') within group ( order by t ) as range
from ( select a.*, b.t
from the_times a
cross join all_times b
where b.t between a.starttime_x and a.endtime_x
)
group by starttime, endtime, 'x' || starttime_x, 'x' || endtime_x;
STARTTIME ENDTIME STA END RANGE
--------- ------- --- --- ---------------------------------------------------------------------------
00:30 07:30 x1 x16 x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16
16:00 19:00 x33 x39 x33, x34, x35, x36, x37, x38, x39
19:00 20:00 x39 x41 x39, x40, x41
如您所见,这并不是特别漂亮...
请注意,我x
后来添加了。向数字添加任意字符只会使您必须做的算术更加困难。在末尾添加它是解决此问题的一种方法。