【问题标题】:Select part of duration that occurs in specific hour of day选择在一天中的特定时间发生的部分持续时间
【发布时间】:2020-03-18 05:34:37
【问题描述】:

在 SQL Server 中,我有一个包含开始时间和结束时间的进程表,我可以从中使用 DATEDIFF 计算持续时间。

Name        StartTime           EndTime
------------------------------------------------
process1    2016-10-10 11:10    2016-10-10 11:20
process2    2016-10-10 11:40    2016-10-10 12:30

如何以秒为单位选择在一天中的特定时间(11 和 12)发生的进程持续时间的时间跨度?

所以 process1 将是第 11 小时的 10 分钟。 process2 在第 11 小时是 20 分钟,在第 12 小时是 30 分钟。

【问题讨论】:

    标签: sql-server sql-server-2008 tsql sql-server-2008-r2


    【解决方案1】:

    以下示例将为每个源记录生成每小时记录。它使用递归 CTE 从每条记录的 StartTime 移动到 EndTime。它可能需要稍作修改才能使其适用于您的情况,但希望您能了解此方法的工作原理。

    请注意,如下例所示,即使时间跨度跨越日期边界,它也能正常工作。

    --==================================================================================
    -- Do some quick setup to get a temporary table populated with data to use:
    --==================================================================================
    IF OBJECT_ID('tempdb..#ProcessHistory', 'U') IS NOT NULL DROP TABLE #ProcessHistory;
    CREATE TABLE #ProcessHistory (
        Name VARCHAR(20),
        StartTime DATETIME,
        EndTime DATETIME
    )
    
    INSERT INTO #ProcessHistory
    VALUES  ('process1', '2016-10-10 11:10', '2016-10-10 11:20'),
            ('process2', '2016-10-10 11:40', '2016-10-10 12:30'),
            ('process3', '2016-10-10 22:21', '2016-10-11 02:36');
    
    --==================================================================================
    -- Use a recursive CTE to generate hourly data for each record:
    --==================================================================================
    WITH HourlyData AS (
        -- Anchor:
        SELECT
            ph.Name [ProcessName],
            ph.StartTime [StartTime],
            ph.EndTime [EndTime],
            -- Get the current hour with date:
            DATEADD(MINUTE, -DATEPART(MINUTE, ph.StartTime), ph.StartTime) [CurrentHour],
            -- Calculate the next hour for use later:
            DATEADD(MINUTE, 60 - DATEPART(MINUTE, ph.StartTime), ph.StartTime) [NextHour],
            -- Determine how many minutes the process was active this hour:
            CASE
                WHEN DATEDIFF(MINUTE, ph.StartTime, ph.EndTime) > 60 - DATEPART(MINUTE, ph.StartTime)
                    THEN 60 - DATEPART(MINUTE, ph.StartTime)
                ELSE DATEDIFF(MINUTE, ph.StartTime, ph.EndTime)
            END [Minutes]
        FROM #ProcessHistory ph
    
        UNION ALL
    
        -- Recurse:
        SELECT
            hd.ProcessName,
            hd.StartTime,
            hd.EndTime,
            hd.NextHour [CurrentHour],
            DATEADD(HOUR, 1, hd.NextHour) [NextHour],
            -- Determine how many minutes the process was active this hour:
            CASE
                WHEN DATEDIFF(MINUTE, hd.NextHour, hd.EndTime) < 60
                    THEN DATEDIFF(MINUTE, hd.NextHour, hd.EndTime)
                ELSE 60
            END
        FROM HourlyData hd
        WHERE hd.NextHour < hd.EndTime
    )
    SELECT
        hd.ProcessName,
        hd.CurrentHour [HourWithDate],
        CONVERT(DATE, hd.CurrentHour) [Date],
        DATEPART(HOUR, hd.CurrentHour) [Hour],
        hd.Minutes
    FROM HourlyData hd
    ORDER BY
        hd.ProcessName,
        hd.CurrentHour;
    

    上述示例的输出如下所示:

    ProcessName HourWithDate            Date        Hour    Minutes
    process1    2016-10-10 11:00:00.000 2016-10-10  11      10
    process2    2016-10-10 11:00:00.000 2016-10-10  11      20
    process2    2016-10-10 12:00:00.000 2016-10-10  12      30
    process3    2016-10-10 22:00:00.000 2016-10-10  22      39
    process3    2016-10-10 23:00:00.000 2016-10-10  23      60
    process3    2016-10-11 00:00:00.000 2016-10-11  0       60
    process3    2016-10-11 01:00:00.000 2016-10-11  1       60
    process3    2016-10-11 02:00:00.000 2016-10-11  2       36
    

    【讨论】:

    • 感谢@Soukai,这似乎可以解决问题,或者至少为我指明了正确的方向。
    【解决方案2】:

    要处理一般情况,您可以尝试类似

        --drop table #processes 
    CREATE TABLE #processes 
        (
        name  varchar(50),
        StartTime  Datetime,
        EndTime  DateTime
        );
    
        insert #processes VALUES('proc1','20161010 11:10','20161010 11:20');
        insert #processes VALUES('proc2','20161010 11:40','20161010 12:20');
        insert #processes VALUES('proc3','20161010 10:40','20161010 12:20');
    
    ;WITH HRS   AS  (SELECT 0 HR 
                    UNION ALL
                    SELECT HR + 1 FROM HRS WHERE HR < 23),
        MINS    AS  (SELECT 0 MN 
                    UNION ALL
                    SELECT MN + 1 FROM MINS WHERE MN < 59),
        TIMES   AS  (SELECT HR,MN FROM HRS CROSS JOIN MINS)
        SELECT name,starttime,endtime,Count(0) AS mins FROM #processes
                        JOIN TIMES 
                                ON (HR > datepart(hh,Starttime) 
                                    OR HR = datepart(hh,Starttime)   AND MN >= datepart(n,STARTtIME))
                                    AND 
                                    (HR < datepart(hh, EndTime) 
                                    OR HR = datepart(hh, EndTime)  AND MN < datepart(n,EndTime))
    
                        WHERE HR = 11 --hour is 11
    
            GROUP BY name,
                        starttime,
                        endtime                     
    
    
    
        drop table #processes;          
    

    【讨论】:

      【解决方案3】:
      create table #temp (Name varchar(5), starttime datetime, EndTime datetime)
      
      insert into #temp values(1,'2016-10-10 11:10','2016-10-10 11:20' )
      insert into #temp values(2,'2016-10-10 11:40','2016-10-10 12:30' )
      insert into #temp values(2,'2016-10-10 10:40','2016-10-10 11:30' )
      insert into #temp values(2,'2016-10-10 10:40','2016-10-10 12:30' )  
      
      
          DECLARE @firstTime time ,@secondTime time
          set @firstTime ='11:00'
          set @secondTime ='12:00'
      
          select
           CASE WHEN CONVERT(time(0), starttime) < @firstTime AND CONVERT(time(0), EndTime) > @secondTime  THEN  DATEDIFF(ss,@firstTime,@secondTime)
                WHEN CONVERT(time(0), EndTime) > @secondTime THEN DATEDIFF(ss,CONVERT(time(0), starttime),@secondTime)
                WHEN CONVERT(time(0), starttime) < @firstTime THEN DATEDIFF(ss,CONVERT(time(0), EndTime),@secondTime)
               ELSE DATEDIFF(ss,starttime,EndTime) 
           END
          from #temp
      

      【讨论】:

        【解决方案4】:
          SELECT CAST(DATEADD(MINUTE,DATEDIFF(MINUTE,'2016-10-10 11:10','2016-10-10 12:20'),'2011-01-01 00:00:00.000') AS TIME)
        as timeDifference
        

        有时间差-

        SELECT CAST(DATEADD(MINUTE,DATEDIFF(MINUTE,StartTime,EndTime),'2011-01-01 00:00:00.000') AS TIME)
        as timeDifference 
        from #YourTableName
        

        有日期和时间差异

            declare @start_time as varchar(150);
            declare @end_time as varchar(150);
            set @start_time='2016-10-10 10:10';
            set @end_time='2016-10-12 12:10'
        
            SELECT datediff(day,@start_time,@end_time) as dayDifference,
        CAST(DATEADD(MINUTE,DATEDIFF(MINUTE,@start_time,@end_time),'2011-01-01 00:00:00') AS TIME(0))
         as timeDifference
        

        【讨论】:

          【解决方案5】:

          我认为这可以解决问题,但它很丑陋。也许有人可以做得更优雅?

          SELECT 
             case 
               when HOUR(starttime) < 11 AND HOUR(endtime) = 11  then minute(endtime)
               when HOUR(starttime) < 11 AND HOUR(endtime) > 11  then 60
               when HOUR(starttime) = 11 AND HOUR(endtime) = 11  then minute(endtime) - minute(starttime)
               when HOUR(starttime) = 11 AND HOUR(endtime) > 11  then 60 - minute(starttime)
               else 0
             end AS ProcessTimeHour_11,
              case 
               when HOUR(starttime) < 12 AND HOUR(endtime) = 12  then minute(endtime)
               when HOUR(starttime) < 12 AND HOUR(endtime) > 12  then 60
               when HOUR(starttime) = 12 AND HOUR(endtime) = 12  then minute(endtime) - minute(starttime)
               when HOUR(starttime) = 12 AND HOUR(endtime) > 12  then 60 - minute(starttime)
               else 0
             end AS ProcessTimeHour_12
          from StuffAndThings
          

          【讨论】:

            猜你喜欢
            • 2020-09-15
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多