从单行sql创建多行

时间:2020-12-21 01:35:57

i have table structure like

我有桌子结构

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

If Ticket Open Time OR Ticket Closed Time is lies between '20:00:00' and '06:00:00' i.e. 8 PM & 6 AM then new row should get created which does not have that time frame

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

e.g. for first row in above table

例如对于上表中的第一行

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)

I have written 20:00 instead of 20:54 because 54 min. are after 8 PM for 19th feb.

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

2 个解决方案

#1


1  

A great question! Please check my try:

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

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

#2


2  

You can do this using a recursive CTE. The following code shows how. For the test data in CTE A, use something like

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

SELECT ATM, [Ticket Open Time], [Ticket Close Time] FROM Table1

The test data is here to show that all cases have been taken care of. If you don't want case ATM = W included, or you want to adjust the start date, you can modify the SQL accordingly.

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

Also, I used an old-fashioned technique to get the date portion of the datetime. Again, adjust according to the version of SQL Server you're on.

此外,我使用了一种老式的技术来获取日期时间的日期部分。再次,根据您所在的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  

A great question! Please check my try:

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

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

#2


2  

You can do this using a recursive CTE. The following code shows how. For the test data in CTE A, use something like

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

SELECT ATM, [Ticket Open Time], [Ticket Close Time] FROM Table1

The test data is here to show that all cases have been taken care of. If you don't want case ATM = W included, or you want to adjust the start date, you can modify the SQL accordingly.

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

Also, I used an old-fashioned technique to get the date portion of the datetime. Again, adjust according to the version of SQL Server you're on.

此外,我使用了一种老式的技术来获取日期时间的日期部分。再次,根据您所在的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]