0

我有一个表,我们在其中存储类似于以下的数据(开始时间和结束时间都是 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 值?

4

3 回答 3

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_xendtime_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后来添加了。向数字添加任意字符只会使您必须做的算术更加困难。在末尾添加它是解决此问题的一种方法。

于 2013-03-01T09:11:28.237 回答
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
于 2013-03-01T09:05:07.280 回答
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;
于 2013-03-01T14:10:05.770 回答