【问题标题】:Splitting time + duration into intervals in t-sql在 t-sql 中将时间 + 持续时间拆分为间隔
【发布时间】:2010-09-20 03:35:44
【问题描述】:

有人知道解决这个问题的简单方法吗?

我有一个表格,其中包含事件的开始时间和相关的持续时间。我需要能够将活动持续时间分成 30 分钟的间隔。例如,如果一个事件在 10:45:00 开始并且持续时间是 00:17:00,那么返回的集合应该分配 15 分钟到 10:30:00 间隔和 00:02:00 分钟到 11:00 :00 间隔。

我确信我可以找出一个笨拙的方法,但我想要一些更简单的方法。我想这一定经常出现,但谷歌今天无济于事。

谢谢,

史蒂夫

【问题讨论】:

    标签: tsql datetime


    【解决方案1】:

    您可以创建一个仅包含时间(超过 24 小时)的查找表,然后加入该表。您需要将日期重新设置为查找中使用的日期。然后在上下间隔上执行 datediff 以计算出它们的持续时间。每个中间间隔为 30 分钟。

    create table #interval_lookup (
      from_date datetime,
      to_date datetime
    )
    
    declare @time datetime
    set @time = '00:00:00'
    
    while @time < '2 Jan 1900'
      begin
        insert into #interval_lookup values (@time, dateadd(minute, 30, @time))
        set @time = dateadd(minute, 30, @time)
      end
    
    declare @search_from datetime
    declare @search_to datetime
    
    set @search_from = '10:45:00'
    set @search_to = dateadd(minute, 17, @search_from) 
    
    select
      from_date as interval,
      case
        when from_date <= @search_from and 
             @search_from < to_date and 
             from_date <= @search_to and 
             @search_to < to_date 
             then datediff(minute, @search_from, @search_to)
        when from_date <= @search_from and 
             @search_from < to_date 
             then datediff(minute, @search_from, to_date)
        when from_date <= @search_to and 
             @search_to < to_date then 
             datediff(minute, from_date, @search_to)
        else 30
      end as duration
    from
      #interval_lookup
    where
      to_date > @search_from
      and from_date <= @search_to
    

    【讨论】:

      【解决方案2】:

      创建拆分单个事件的 TVF:

      ALTER FUNCTION dbo.TVF_TimeRange_Split_To_Grid
      (
          @eventStartTime datetime
          , @eventDurationMins float
          , @intervalMins int
      )
      RETURNS @retTable table
      (
          intervalStartTime datetime
          ,intervalEndTime datetime
          ,eventDurationInIntervalMins float
      )
      AS
      BEGIN
      
          declare @eventMinuteOfDay int
          set @eventMinuteOfDay = datepart(hour,@eventStartTime)*60+datepart(minute,@eventStartTime)
      
          declare @intervalStartMinute int
          set @intervalStartMinute = @eventMinuteOfDay - @eventMinuteOfDay % @intervalMins
      
          declare @intervalStartTime datetime
          set @intervalStartTime = dateadd(minute,@intervalStartMinute,cast(floor(cast(@eventStartTime as float)) as datetime))
      
          declare @intervalEndTime datetime
          set @intervalEndTime = dateadd(minute,@intervalMins,@intervalStartTime)
      
          declare @eventDurationInIntervalMins float
      
          while (@eventDurationMins>0)
          begin
      
              set @eventDurationInIntervalMins = cast(@intervalEndTime-@eventStartTime as float)*24*60
              if @eventDurationMins<@eventDurationInIntervalMins 
                  set @eventDurationInIntervalMins = @eventDurationMins
      
              insert into @retTable
              select @intervalStartTime,@intervalEndTime,@eventDurationInIntervalMins
      
              set @eventDurationMins = @eventDurationMins - @eventDurationInIntervalMins
              set @eventStartTime = @intervalEndTime
      
              set @intervalStartTime = @intervalEndTime
              set @intervalEndTime = dateadd(minute,@intervalMins,@intervalEndTime)
          end
      
          RETURN 
      END
      GO
      

      测试:

      select getdate()
      select * from dbo.TVF_TimeRange_Split_To_Grid(getdate(),23,30)
      

      测试结果:

      2008-10-31 11:28:12.377
      
      intervalStartTime       intervalEndTime         eventDurationInIntervalMins
      ----------------------- ----------------------- ---------------------------
      2008-10-31 11:00:00.000 2008-10-31 11:30:00.000 1,79372222222222
      2008-10-31 11:30:00.000 2008-10-31 12:00:00.000 21,2062777777778
      

      示例用法:

      select input.eventName, result.* from
      (
          select 
              'first' as eventName
              ,cast('2008-10-03 10:45' as datetime) as startTime
              ,17 as durationMins
          union all
          select 
              'second' as eventName
              ,cast('2008-10-05 11:00' as datetime) as startTime
              ,17 as durationMins
          union all
          select 
              'third' as eventName
              ,cast('2008-10-05 12:00' as datetime) as startTime
              ,100 as durationMins
      ) input
      cross apply dbo.TVF_TimeRange_Split_To_Grid(input.startTime,input.durationMins,30) result
      

      示例使用结果:

      eventName intervalStartTime       intervalEndTime         eventDurationInIntervalMins
      --------- ----------------------- ----------------------- ---------------------------
      first     2008-10-03 10:30:00.000 2008-10-03 11:00:00.000 15
      first     2008-10-03 11:00:00.000 2008-10-03 11:30:00.000 2
      second    2008-10-05 11:00:00.000 2008-10-05 11:30:00.000 17
      third     2008-10-05 12:00:00.000 2008-10-05 12:30:00.000 30
      third     2008-10-05 12:30:00.000 2008-10-05 13:00:00.000 30
      third     2008-10-05 13:00:00.000 2008-10-05 13:30:00.000 30
      third     2008-10-05 13:30:00.000 2008-10-05 14:00:00.000 10
      
      (7 row(s) affected)
      

      【讨论】:

      • 我很喜欢这种方法,让我的看起来有点难看。我想知道性能差异是什么样的?
      • 我只需要编辑我的灵魂,所以它看起来也有点难看;)。我之前误读了问题要求。而且我相信您的解决方案会执行得更快。
      • 但是,我的间隔长度是动态的;)
      猜你喜欢
      • 2018-04-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-27
      • 1970-01-01
      相关资源
      最近更新 更多