简体   繁体   English

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

[英]SQL Server: Finding date given EndDate and # Days, excluding days from specific date ranges

I have a TableA in a database similar to the following: 我在数据库中有一个类似于以下内容的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

Imagine it's keeping track of a specific user's "Away" days. 想象一下,它一直在跟踪特定用户的“离开”日。 Given the following Inputs: 给定以下输入:

  1. SomeEndDate (Date), SomeEndDate (日期),
  2. NumDays (Integer) NumDays (整数)

I want to find the SomeStartDate (Date) that is Numdays non-illness days from EndDate . 我想从EndDate找到Numdays非疾病日的SomeStartDate (Date)。 In other words, say I am given a SomeEndDate value '2014-03-10' and a NumDays value of 60; 换句话说,假设我的SomeEndDate值为'2014-03-10', NumDays值为60; the matching SomeStartDate would be: 匹配的SomeStartDate将是:

  1. 2014-03-10 to 2014-03-09 = 1 2014-03-10至2014-03-09 = 1
  2. 2014-03-08 to 2014-01-01 = 57 2014-03-08至2014-01-01 = 57
  3. 2013-05-05 to 2013-05-03 = 2 2013-05-05至2013-05-03 = 2

So, at 60 non-illness days, we get a SomeStartDate of '2013-05-03'. 因此,在60个非疾病日,我们得到的SomeStartDate为'2013-05-03'。 IS there any easy way to accomplish this in SQL? 有没有简单的方法可以在SQL中完成此操作? I imagine I could loop each day, check whether or not it falls into one of the illness ranges, and increment a counter if not (exiting the loop after counter = @numdays)... but that seems wildly inefficient. 我想我可以每天循环一次,检查它是否属于疾病范围之一,如果不是,则增加一个计数器(在counter = @numdays之后退出循环)...但是这似乎效率很低。 Appreciate any help. 感谢任何帮助。

Make a Calendar table that has a list of all the dates you will ever care about. 制作一个日历表,其中包含您将要关心的所有日期的列表。

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

The Calendar table method lets you also easily exclude holidays, weekends, etc. 通过“日历”表方法,您还可以轻松排除假期,周末等。

SQL Server 2012: SQL Server 2012:

Try this solution: 试试这个解决方案:

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
*/

Note #1: LEAD(End) will return the previous End date (previous because of ORDER BY End DESC) 注意#1: LEAD(End)将返回上一个结束日期(由于ORDER BY End DESC而返回上一个)

Note #2: DATEDIFF(DAY, RangeStart, RangeStop) computes the num. 注意#2: DATEDIFF(DAY, RangeStart, RangeStop)计算数字。 of days between current start (alias x.RangeStop ) and "previous" end (alias x.RangeStar ) => Current_NumOfDays 当前开始(alias x.RangeStop )和“上一个”结束(alias x.RangeStar )之间的天数=> Current_NumOfDays

Note #3: SUM( Current_NumOfDays ) computes a running total thus: 1 + 66 + (3) 注意#3: SUM( Current_NumOfDays )计算运行总计,因此:1 + 66 +(3)

Note #4: I've used @NumOfDays = 70 (not 60 ) 注意#4:我用@NumOfDays = 70 (不是60

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM