[英]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:
给定以下输入:
SomeEndDate
(Date), SomeEndDate
(日期), 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将是:
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.