如何将2列之间的值转换为csv文本

时间:2013-03-01 00:30:16

标签: sql oracle oracle10g

我有一个表,我们存储类似于以下的数据(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值?

3 个答案:

答案 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_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。在数字中添加任意字符只会使你必须做更多困难。最后添加它是解决这个问题的方法。

答案 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;