2014-06-30 65 views
0

我有一个数据库TableA类似以下内容:的SQL Server:查找日期给出结束日期和#天,具体日期不包括天范围

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(整数)

我想找到SomeStartDate(日期),即从EndDateNumdays非发病天。换句话说,假设我给出了一个SomeEndDate值'2014-03-10'和一个NumDays值60;匹配SomeStartDate将是:

  1. 2014年3月10日2014年3月9日向= 1
  2. 2014年3月8日到2014年1月1日= 57
  3. 2013年5月5日到2013-05-03 = 2

因此,在60个非疾病日期,我们得到'2013-05-03'的SomeStartDate。有什么简单的方法可以在SQL中完成这个任务吗?我想我可以每天循环,检查它是否落入其中一个疾病范围,并且如果没有,则增加一个计数器(在计数器= @numdays之后退出循环)......但这看起来非常低效。感谢任何帮助。

+1

不完全确定我理解你的表,但我想我会尝试在两个单独的计算中做到这一点。从您的enddate减去您的天数。计算疾病日数,并将其添加到您之前的结果中。 – Andrew

+0

要非常小心你如何解释范围。通常,仅限日期的范围是完全关闭的时间间隔。例如,3月9日至3月10日通常会被视为*两天*,而不是一天。 (当然,如果涉及时间的话,那就完全不同了。) –

+0

安德鲁 - 我早就想过了,但是无法说服自己这是好方法(也许我错了!)。如果我从EndDate中抽取60天并添加@count疾病日期以获得StartDate = dateadd(d, - (60 + @ count),EndDate),那么我的新StartDate可能会落在另一个疾病范围内,或者包含额外的疾病天。所以我会循环这个,直到新的StartDate没有落在疾病范围内或包含额外的疾病日子? – AliceSmith

回答

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 

的日历表的方法可以让你也易排除节假日,周末等

+0

我很欣赏这个建议! :)它肯定比我想要的更清洁。我现在就试试看。 – AliceSmith

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结束DESC)

注#2:DATEDIFF(DAY, RangeStart, RangeStop)计算数字。当前启动(别名x.RangeStop)和 “前一” 端(别名x.RangeStar)=>Current_NumOfDays

说明#3之间的天:SUM(Current_NumOfDays)计算从而运行总计:1 + 66 +(3)

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

相关问题