【问题标题】:Counting days between multiple start-stop records within a calendar period计算一个日历期间内多个起止记录之间的天数
【发布时间】:2014-07-01 21:14:31
【问题描述】:

一个活动可以在给定的时间段内启动和停止 0 次或多次。每次执行时,都会生成带有日期和操作的记录——我将这些操作称为“激活 (A)”和“停用 (D)”。我需要总结给定月份中“活跃”天数的总数。我想要一种简单的方法来做到这一点,而不会出现异常和特殊情况。我必须在 tsql 中执行此操作。

这是我目前采用的程序方法:

  1. 在此期间是否有开始或停止事件? a) 如果否,则查找该时间段之前的最新事件 i) 如果该事件为开始,则该期间的所有日期都处于活动状态 ii) 如果事件是停止,则期间没有活动天数 iii) 完成 b) 如果是...那么程序检查和 if-thens 的老鼠巢。

像往常一样,我想了解其他人如何看待问题,而不是开始编写例程以按时间顺序对记录进行排序并循环遍历它们。

一个典型的数据集:

6/11/2014  A
6/20/2014  D
6/24/2014  A
6/26/2014  D
6/29/2014  A

在这里,感兴趣的时间段是 6 月,第一个操作是激活的 - 因此该活动在 6 月 11 日之前一直处于非活动状态。同样,在 6/29,事情再次活跃,因此该月的剩余时间被视为活跃。在 D 和 A 日期上使用 datediff() 函数,我得到 9 + 2,该月的最后一天总共有 12 个活动天。

同样,对于一个周期的数据集,可能有 0 条或更多条记录。

当然,数据也可以如下所示:

6/11/2014  D
6/20/2014  A
6/24/2014  D
6/26/2014  A
6/29/2014  D

在这里,活动在 6 月 11 日被停用之前一直处于“活动状态”。简单地 datediff()'ing (D - A) 日期给我额外的 4 + 3 天,总共 17 天。

我担心如果我选择,排序然后做,我会被车轴缠住。我想知道其他人可能会采取什么更巧妙的方法。

【问题讨论】:

    标签: sql sql-server date


    【解决方案1】:

    此查询将为您提供给定期间的所有日期以及当天的相应事件:

    SQL Fiddle

    MS SQL Server 2008 架构设置

    CREATE TABLE Table1
        ([dt] date, [event] varchar(1))
    ;
    
    INSERT INTO Table1
        ([dt], [event])
    VALUES
        ('2014-06-11', 'A'),
        ('2014-06-20', 'D'),
        ('2014-06-24', 'A'),
        ('2014-06-26', 'D'),
        ('2014-06-29', 'A')
    ;
    

    查询 1

    declare @start date, @end date
    select @start = '2014-06-01', @end = '2014-06-30'
    ;with dates as (
      SELECT @start AS d, (SELECT TOP 1 CASE WHEN event = 'A' THEN 'D' ELSE 'A' END FROM Table1 ORDER BY dt) AS e
      UNION ALL 
      SELECT dateadd(day, 1, d), COALESCE((SELECT event FROM Table1 WHERE dt = dateadd(day, 1, dates.d)), dates.e)
      FROM dates 
      WHERE dates.d < @end)
    select * from dates
    OPTION (MAXRECURSION 0)
    

    Results

    |          D | E |
    |------------|---|
    | 2014-06-01 | D |
    | 2014-06-02 | D |
    | 2014-06-03 | D |
    | 2014-06-04 | D |
    | 2014-06-05 | D |
    | 2014-06-06 | D |
    | 2014-06-07 | D |
    | 2014-06-08 | D |
    | 2014-06-09 | D |
    | 2014-06-10 | D |
    | 2014-06-11 | A |
    | 2014-06-12 | A |
    | 2014-06-13 | A |
    | 2014-06-14 | A |
    | 2014-06-15 | A |
    | 2014-06-16 | A |
    | 2014-06-17 | A |
    | 2014-06-18 | A |
    | 2014-06-19 | A |
    | 2014-06-20 | D |
    | 2014-06-21 | D |
    | 2014-06-22 | D |
    | 2014-06-23 | D |
    | 2014-06-24 | A |
    | 2014-06-25 | A |
    | 2014-06-26 | D |
    | 2014-06-27 | D |
    | 2014-06-28 | D |
    | 2014-06-29 | A |
    | 2014-06-30 | A |
    

    使用上面的查询,您将能够轻松地生成计数查询,如下所示:

    declare @start date, @end date
    select @start = '2014-06-01', @end = '2014-06-30'
    ;with dates as (
      SELECT @start AS d, (SELECT TOP 1 CASE WHEN event = 'A' THEN 'D' ELSE 'A' END FROM Table1 ORDER BY dt) AS e
      UNION ALL 
      SELECT dateadd(day, 1, d), COALESCE((SELECT event FROM Table1 WHERE dt = dateadd(day, 1, dates.d)), dates.e)
      FROM dates 
      WHERE dates.d < @end)
    select e, count(*) from dates GROUP BY e
    OPTION (MAXRECURSION 0)
    

    结果

    | E | COLUMN_1 |
    |---|----------|
    | A |       13 |
    | D |       17 |
    

    或者像这样:

    declare @start date, @end date
    select @start = '2014-06-01', @end = '2014-06-30'
    ;with dates as (
      SELECT @start AS d, (SELECT TOP 1 CASE WHEN event = 'A' THEN 'D' ELSE 'A' END FROM Table1 ORDER BY dt) AS e
      UNION ALL 
      SELECT dateadd(day, 1, d), COALESCE((SELECT event FROM Table1 WHERE dt = dateadd(day, 1, dates.d)), dates.e)
      FROM dates 
      WHERE dates.d < @end)
    select 
    sum(case when e='A' THEN 1 ELSE 0 END) as A_days,
    sum(case when e='D' THEN 1 ELSE 0 END) as D_days
    from dates
    OPTION (MAXRECURSION 0)
    

    结果

    | A_DAYS | D_DAYS |
    |--------|--------|
    |     13 |     17 |
    

    【讨论】:

    • 在 WITH 之后将 table1 定义为 CTE 是否合法,如下所示: WITH table1 (d, t) AS (...), dates AS ( [your definition above] )?我还没有看到在同一 WITH 范围内混合非递归和递归定义的参考。
    • 不明白你的问题
    • 你去吧:sqlfiddle.com/#!3/d41d8/36209我想你可以自己试试......
    • 是的——我做到了,而且它完全按照我的需要......完美地工作。
    • 顺便说一句,如果你注意到我犯了一个错误。您需要自己修复它(非常容易修复)。如果您在第 1 天有一个事件(即,如果您的表中存在 @start 日期的事件)dates CTE 中的初始 e 值将被错误地设置为相反的事件。很容易修复,我会留给你自己
    猜你喜欢
    • 2021-05-25
    • 1970-01-01
    • 2015-02-26
    • 2019-09-27
    相关资源
    最近更新 更多