【问题标题】:SQL Count sickness daysSQL 统计病假天数
【发布时间】:2015-02-14 15:34:15
【问题描述】:

我有一个 SQL Server 表,其中包含所有员工及其疾病的列表。

我需要能够计算出他们在当前季度生病了多少天

问题是,有些人可能已经病了一年,例如,FROMDATE 可能是 2013-12-31,而 UNTILDATE 可能是 2014-12-31(1 年病假)。但是,它应该只计算当前季度发生的疾病的天数。所以应该是生病 90 天左右,而不是一整年。

当前 SQL

select SUM(a.WORKDAYS) as Total
from ABSENCE a
where a.FROMDATE < GETDATE() and 

a.UNTILDATE > DATEADD(MONTH, -3, GETDATE())
and
a.ABS_REASON='SICK'

因此,目前,它取自任何正确的起始日期,因为我需要考虑在本季度开始之前已经生病但进入当前季度仍然生病的人,但应该只计算从何时开始的天数季度开始到季度结束。

任何帮助将不胜感激。

【问题讨论】:

  • 可以分享ABSENCE表的样本数据吗?
  • 您确实需要在季度结束之前开始并在季度开始之后结束的所有缺勤(或者如果您不知道他们何时回来,则为 NULL),所以你的WHERE 子句是正确的(除非你确实需要满足NULLUNTILDATE)。然后,您只需要计算当前季度内的天数和缺勤范围,为此您需要知道哪些日期是工作日,正如 Rob Farley 所建议的那样。

标签: sql sql-server sum case case-when


【解决方案1】:

使用日期表,您可以轻松找到日期在您感兴趣的两个日期之间的日期计数,以及围绕该日期的休假期。您还可以过滤日期以排除非工作日和公共假期。

有很多方法可以生成这样的日期表,并且在 stackoverflow 和 dba.stackexchange 上都有很多描述。

【讨论】:

    【解决方案2】:

    不确定您的列。您应该只提供在 2013-12-31 和 2014-12-31 之间提供记录的 sql,然后询问您的问题。

    试试这个,

     select SUM(Case when datepart(MM, a.FROMDATE) IN (10,11,12) Then a.WORKDAYS Else  End) 
    as Total
       from ABSENCE a
       where a.FROMDATE >= '2013-12-31' and 
       a.UNTILDATE <= '2014-12-31'
       and
       a.ABS_REASON='SICK'
    

    【讨论】:

      【解决方案3】:
      SELECT SUM(a.WORKDAYS) as Total
      FROM ABSENCE a
      WHERE (a.FROMDATE >= DATEADD(MONTH, -3, GETDATE()) OR a.UNTILDATE >= DATEADD(MONTH, -3, GETDATE()))
      AND a.ABS_REASON = 'SICK'
      

      特定季度

      SELECT SUM(a.WORKDAYS) as Total
      FROM ABSENCE a
      WHERE (a.FROMDATE >= DATEADD(quarter, -1, GETDATE()) OR a.UNTILDATE >= DATEADD(quarter, -1, GETDATE()))
      AND a.ABS_REASON = 'SICK'
      

      【讨论】:

      • 这不起作用,因为如果有人长期生病,起始日期可能是一年多以前。它应该只计算当前季度范围内的病假天数
      • 逻辑是只统计最近3个月的病假天数?
      【解决方案4】:

      拥有一个包含所有可能日期列表的日历表很方便,但在这种情况下,我们可以不用它。

      我会概括一下您的问题。让我们有两个参数来定义您感兴趣的日期范围,而不是只查看当前季度:

      DECLARE @ParamStartDate date;
      DECLARE @ParamEndDate date;
      

      首先,我们需要从Absence 获取范围从FromDateUntilDate 且与给定时间段相交的所有行。

      SELECT
          ...
      FROM
          Absence
      WHERE
          ABS_REASON='SICK'
          -- all absence periods, which overlap with the given period
          AND FromDate <= @ParamEndDate
          AND UntilDate >= @ParamStartDate
      

      (StartA &lt;= EndB)(EndA &gt;= StartB) 时,两个句点 A 和 B 重叠。

      然后我们需要计算两个时期的交集有多少天。

      相交时间段不能大于给定的日期范围(@ParamStartDate@ParamEndDate)。

      交叉期不能大于疾病的持续时间(FromDateUntilDate)。

      所以,相交的开始是FromDate@ParamStartDate中最晚的一个,即MAX(FromDate, @ParamStartDate)

      路口的终点是UntilDate@ParamEndDate中最早的,即MIN(UntilDate, @ParamEndDate)

      最后,以天为单位的路口持续时间为

      DATEDIFF(day, MAX(FromDate, @ParamStartDate), MIN(UntilDate, @ParamEndDate))
      

      但是,只有当它是积极的。如果为负数,则表示病假期在给定季度开始之前结束(或病假在给定季度结束后开始)。

      没有内置的 MIN、MAX 函数可以根据需要使用两个参数,所以我使用CROSS APPLY 来计算它们。另外,为了完整起见,我计算了给定季度的天数。最终查询如下所示:

      SELECT
          1+DATEDIFF(day, @ParamStartDate, @ParamEndDate) AS QuarterDays
          ,CASE WHEN 1+DATEDIFF(day, CTE_MaxStartDate.AbsenceStartDate, CTE_MinEndDate.AbsenceEndDate) > 0
              THEN 1+DATEDIFF(day, CTE_MaxStartDate.AbsenceStartDate, CTE_MinEndDate.AbsenceEndDate)
              ELSE 0 END AS AbsenceDays
      FROM
          Absence
          CROSS APPLY
          (
              SELECT CASE WHEN UntilDate < @ParamEndDate THEN UntilDate ELSE @ParamEndDate END AS AbsenceEndDate
          ) AS CTE_MinEndDate
          CROSS APPLY
          (
              SELECT CASE WHEN FromDate > @ParamStartDate THEN FromDate ELSE @ParamStartDate END AS AbsenceStartDate
          ) AS CTE_MaxStartDate
      WHERE
          ABS_REASON='SICK'
          -- all absence periods, which overlap with the given period
          AND FromDate <= @ParamEndDate
          AND UntilDate >= @ParamStartDate
      

      如果期间的开始日期和结束日期相同,我将 1 添加到 DATEDIFF 以获得一天的持续时间。

      【讨论】:

      • @DavidHayward,你最终是如何解决这个问题的?提供的任何答案对您有用吗?如果是,您可以投票给他们并接受一个最有用的答案。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多