我有一个表,我们存储类似于以下的数据(Starttime和Endtime都是varchar2):
StartTime EndTime
00:30 07:30
16:00 19:00
. .
. .
. .
我想要做的是将其转换为相应的半小时数字,以便:
00:00 becomes x1
00:30 x2
01:00 x3
01:30 x4
..
午夜(00:00是x1,23:30是48)
我使用以下SQL做了什么:
Select StartTime, EndTime,
Case StartTime
When '00:00' Then 'x1'
When '00:30' Then 'x2' ....
End StartTime_x
Case EndTime
When '00:00' Then 'x1'
When '00:30' Then 'x2' ....
End EndTime_x
From myTable
它给了我这个:
StartTime, EndTime, StartTime_x, EndTime_x
16:00 19:00 x33 x39
19:00 20:00 x39 x41
我想要实现的目标是:
StartTime, EndTime, StartTime_x, EndTime_x Range
16:00 19:00 x33 x39 x33,x34,x35,x36,x37,x38,x39
19:00 20:00 x39 x41 x39,x40,x41
如何在starttime_x和endtime_x值之间进行遍历并创建它们之间的csv值?
答案 0 :(得分:1)
这是SQL
中的解决方案我使用为测试插入的值创建的表是:
CREATE TABLE mytable AS
Select '00:30' As Starttime, '02:30' As Endtime FROM dual
Union All
Select '01:00' As Starttime, '04:00' As Endtime FROM dual
Union All
Select '00:00' As Starttime, '03:30' As Endtime FROM dual;
以下是以您期望的格式返回数据的查询
Select X.Starttime,X.Endtime,X.Starttime_X,X.Endtime_X, Listagg(X.Abc,',') Within Group (Order By X.Starttime)
FROM
(
Select distinct a.*, 'x'||(LEVEL + (TO_NUMBER(substr(starttime_x,INSTR(starttime_x,'x',1)+1))) -1) as abc
From (Select StartTime, EndTime,
Case StartTime
When '00:00' Then 'x1'
When '00:30' Then 'x2'
When '01:00' Then 'x3'
When '01:30' Then 'x4'
When '02:00' Then 'x5'
When '02:30' Then 'x6'
When '03:00' Then 'x7'
When '03:30' Then 'x8'
When '04:00' Then 'x9'
When '04:30' Then 'x10'
When '05:00' Then 'x11'
WHEN '05:30' THEN 'x12'
End StartTime_x,
Case EndTime
When '00:00' Then 'x1'
When '00:30' Then 'x2'
When '01:00' Then 'x3'
When '01:30' Then 'x4'
When '02:00' Then 'x5'
When '02:30' Then 'x6'
When '03:00' Then 'x7'
When '03:30' Then 'x8'
When '04:00' Then 'x9'
When '04:30' Then 'x10'
When '05:00' Then 'x11'
WHEN '05:30' THEN 'x12'
End Endtime_X
From Mytable) A
Connect By Level <= (To_Number(Substr(Endtime_X,Instr(Endtime_X,'x',1)+1)) - To_Number(Substr(Starttime_X,Instr(Starttime_X,'x',1)+1))) + 1
) X
GROUP BY x.starttime, x.endtime,X.Starttime_X,X.Endtime_x;
输出结果为:
00:00 03:30 x1 x8 x1,x2,x3,x4,x5,x6,x7,x8
00:30 02:30 x2 x6 x2,x3,x4,x5,x6
01:00 04:00 x3 x9 x3,x4,x5,x6,x7,x8,x9
答案 1 :(得分:1)
首先,您使用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
。在数字中添加任意字符只会使你必须做更多困难。最后添加它是解决这个问题的方法。
答案 2 :(得分:0)
对于任何使用oracle 10g的人,我在此基于Ben的答案添加了解决方案:
我只是用wm_concat改变了listagg:
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
, wm_concat('x' || 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;