从单行sql创建多个行

时间:2013-05-15 10:26:40

标签: sql sql-server-2008

我有像

这样的表结构
ATM         Ticket Open Time    Ticket Closed Time

M30G324202  17-02-2013 06:15    19-02-2013 20:54
M30G324202  28-02-2013 21:00    01-03-2013 11:18
M30G324203  27-02-2013 19:10    28-02-2013 07:14
M30G324203  28-02-2013 07:15    28-02-2013 11:18

如果票务开放时间或票证关闭时间位于'20:00:00'和'06:00:00'之间,即晚上8点&早上6点然后应该创建一个没有该时间范围的新行

e.g。对于上表中的第一行

ATM         Ticket Open Time    Ticket Closed Time

M30G324202  17-02-2013 06:15    17-02-2013 20:00
M30G324202  18-02-2013 06:00    18-02-2013 20:00
M30G324202  19-02-2013 06:00    19-02-2013 20:00

//Above was for Only 1st Row

//Second Row Change AS Follows

M30G324202  01-03-2013 06:00    01-03-2013 11:18 
   (Time From 28-02-2013 21:00 Will get neglected till next day morning 6 AM 
    as it is after 8 PM )

//Third Row Change AS Follows

M30G324203  27-02-2013 19:10    27-02-2013 20:00
M30G324203  28-02-2013 06:00    28-02-2013 07:14

//Fourth Row Change AS Follows

M30G324203  28-02-2013 07:15    28-02-2013 11:18 (No Change as it is)

我写了20:00而不是20:54因为54分钟。是在19点之后的晚上8点之后。

2 个答案:

答案 0 :(得分:2)

您可以使用递归CTE执行此操作。以下代码显示了如何操作。对于CTE A中的测试数据,请使用类似

的内容
SELECT ATM, [Ticket Open Time], [Ticket Close Time] FROM Table1

此处的测试数据表明所有案例都已得到处理。如果您不想包含ATM = W,或者您想调整开始日期,则可以相应地修改SQL。

另外,我使用了一种老式技术来获取datetime的日期部分。再次,根据您所在的SQL Server版本进行调整。

WITH A
AS  (

    SELECT 'X' as ATM
        ,   convert(datetime, '2/17/2013 6:15') as [Ticket Open Time]
        ,   convert(datetime, '2/19/2013 20:54') as [Ticket Close Time]
    UNION ALL 
    SELECT  'Y'
        ,   convert(datetime, '2/24/2013 7:32')
        ,   convert(datetime, '2/25/2013 14:26')
    UNION ALL
    SELECT  'Z'
        ,   convert(datetime, '2/20/2013 9:00')
        ,   convert(datetime, '2/20/2013 13:43')    
    UNION ALL
    SELECT  'W'
        ,   convert(datetime, '3/1/2013 3:34')
        ,   convert(datetime, '3/1/2013 6:45')  
)
,   B
AS  (

    SELECT  ATM
        ,   [Ticket Open Time]
        ,   [Original Ticket Close Time] = A.[Ticket Close Time]
        ,   [Ticket Close Time] = CASE WHEN  DateAdd(hh, 20, convert(datetime, convert(varchar(10), [Ticket Open Time], 101))) > A.[Ticket Close Time]
                 THEN  [Ticket Close Time]
                 ELSE  DateAdd(hh, 20, convert(datetime, convert(varchar(10), [Ticket Open Time], 101)))
            END
    FROM    A
    UNION ALL
    SELECT  ATM
        ,   [Ticket Open Time] = DateAdd(hh, 10, B1.[Ticket Close Time])
        ,   [Original Ticket Close Time] = b1.[Original Ticket Close Time]
        ,   [Ticket Close Time] = CASE
                WHEN DateAdd(hh, 24, b1.[Ticket Close Time]) > b1.[Original Ticket Close Time]
                AND  b1.[Original Ticket Close Time] <= DateAdd(hh,24, b1.[Ticket Close Time])
                THEN b1.[Original Ticket Close Time]
                ELSE DateAdd(hh, 24, b1.[Ticket Close Time])
            END
    FROM    B b1
    WHERE   [Ticket Close Time] < b1.[Original Ticket Close Time]

)
,   C
as  (
    select *
    from    B
    where   [Ticket Open Time] < [Ticket Close Time]
)   
      -- Your actual output
select    ATM, 
            [Ticket Open Time],
        [Ticket Close Time],
 from     C  
 order by  ATM,
         [Ticket Open Time]

答案 1 :(得分:1)

一个很棒的问题! 请检查我的尝试:

declare @tbl as table (ATM nvarchar(20), TicketOpenTime datetime, TicketClosedTime datetime)
insert into @tbl values
('M30G324202', '02-17-2013 06:15', '02-19-2013 20:54'),
('M30G324202', '02-28-2013 21:00', '03-01-2013 11:18'),
('M30G324203', '02-27-2013 19:10', '02-28-2013 07:14'),
('M30G324203', '02-28-2013 07:15', '02-28-2013 11:18')

declare @min datetime, @max datetime
select @min = MIN(TicketOpenTime), @max = max(TicketClosedTime) from @tbl

;with T as(
    select CONVERT(datetime, convert(numeric(20), @min, 101)) dt
    union all
    select dt+1 from T where dt<@max
) 
select 
    a.ATM, 
    case when a.TicketOpenTime>dt1 then a.TicketOpenTime else dt1 end TicketOpenTime,
    case when a.TicketClosedTime>dt2 then dt2 else a.TicketClosedTime end TicketClosedTime
From @tbl a
cross apply(
    select 
        dt, 
        DATEADD(minute, 360, dt) dt1, 
        DATEADD(minute, 1200, dt) dt2 from T b 
    where 
        dt between CAST(a.TicketOpenTime as DATE) and cast(a.TicketClosedTime as DATE)
)x
where a.TicketOpenTime<=x.dt2
order by a.ATM, a. TicketOpenTime