SQL Server:查找给定EndDate和#Days的日期,不包括特定日期范围内的天数

时间:2014-06-30 17:12:05

标签: sql sql-server date

我在数据库中有TableA,类似于以下内容:

Id | Status   | Start       | End

1  | Illness  | 2013-04-02  | 2013-04-23
2  | Illness  | 2013-05-05  | 2014-01-01
3  | Vacation | 2014-02-01  | 2014-03-01
4  | Illness  | 2014-03-08  | 2014-03-09
5  | Vacation | 2014-05-05  | NULL

想象一下,它跟踪特定用户"离开"天。给出以下输入:

  1. SomeEndDate(日期),
  2. NumDays(整数)
  3. 我希望找到距离SomeStartDate Numdays({1}}非疾病日的EndDate(日期)。换句话说,假设我获得SomeEndDate价值' 2014-03-10'和NumDays值为60;匹配的SomeStartDate将是:

    1. 2014-03-10至2014-03-09 = 1
    2. 2014-03-08至2014-01-01 = 57
    3. 2013-05-05至2013-05-03 = 2
    4. 因此,在60个非疾病日,我们得到了SomeStartDate' 2013-05-03'。有没有简单的方法在SQL中实现这一点?我想我可以每天循环,检查它是否属于其中一个疾病范围,如果没有,则增加一个计数器(在计数器= @numdays之后退出循环)......但这似乎非常低效。感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

制作一张日历表,其中列出了您将关注的所有日期。

SELECT MIN([date])
FROM (
  SELECT TOP(@NumDays) [date]
  FROM Calendar c
  WHERE c.Date < @SomeEndDate
    AND NOT EXISTS (
      SELECT 1
      FROM TableA a
      WHERE c.Date BETWEEN a.Start AND a.END
        AND Status = 'Illness'
    )
  ORDER BY c.Date
) t

Calendar表方法还可以轻松排除假期,周末等。

答案 1 :(得分:0)

SQL Server 2012:

试试这个解决方案:

DECLARE @NumDays INT = 70, @SomeEndDate DATE = '2014-03-10';

SELECT  
    [RangeStop],
    CASE 
        WHEN RunningTotal_NumOfDays <= @NumDays THEN [RangeStart]
        WHEN RunningTotal_NumOfDays - Current_NumOfDays <= @NumDays THEN DATEADD(DAY, -(@NumDays - (RunningTotal_NumOfDays - Current_NumOfDays))+1, [RangeStop])
    END AS [RangeStart]
FROM (
    SELECT  
        y.*, 
        DATEDIFF(DAY, y.RangeStart, y.RangeStop) AS Current_NumOfDays,
        SUM( DATEDIFF(DAY, y.RangeStart, y.RangeStop) ) OVER(ORDER BY y.RangeStart DESC) AS RunningTotal_NumOfDays
    FROM (
        SELECT  LEAD(x.[End]) OVER(ORDER BY x.[End] DESC) AS RangeStart, -- It's previous date because of "ORDER BY x.[End] DESC"
            x.[Start] AS RangeStop
        FROM (
            SELECT   @SomeEndDate AS [Start], '9999-12-31' AS [End]
            UNION ALL
            SELECT  x.[Start], x.[End]
            FROM    @MyTable AS x
            WHERE   x.[Status] = 'Illness'
            AND     x.[End] <= @SomeEndDate
        ) x
    ) y
) z
WHERE RunningTotal_NumOfDays - Current_NumOfDays <= @NumDays;

/*
Output:
RangeStop  RangeStart
---------- ----------
2014-03-10 2014-03-09
2014-03-08 2014-01-01
2013-05-05 2013-05-03
*/

注意#1:LEAD(End)将返回上一个结束日期(之前因为ORDER BY End DESC)

注意#2:DATEDIFF(DAY, RangeStart, RangeStop)计算num。当前开始(别名x.RangeStop)和&#34;之前&#34;之间的天数结束(别名x.RangeStar)=&gt; Current_NumOfDays

注意#3:SUM( Current_NumOfDays )计算一个总计:1 + 66 +(3)

注意#4:我使用了@NumOfDays = 70(不是60

相关问题