【问题标题】:Calculating next 3 year anniversary of employee based on join date根据入职日期计算员工下一个 3 周年
【发布时间】:2017-01-24 02:03:40
【问题描述】:

我有一个员工 mysql 数据库表,其中包含员工姓名和开始日期。我希望能够从今天开始计算每个员工即将到来的 3 周年纪念日。 (例如,乔丹的下一个纪念日是 2019 年 8 月 25 日,李的下一个纪念日是 2017 年 9 月 22 日,迈克的下一个纪念日是 2018 年 10 月 26 日)

id    name       startDate        
---------------------------
0     Jordan     08/25/2010          
1     Lee        09/22/2014          
2     Mike       10/26/2015        

低效的蛮力解决方案是生成从 startDate 到当前日期的所有周年日期加上今天日期之后的一个日期,然后选择它。

但是必须有一个更有效的 SQL 查询来根据今天的日期显示每个员工下一个即将到来的周年纪念日。必须以某种方式使用 DATEDIFF。

【问题讨论】:

  • @KamilG 怎么样?从今天开始的下一个周年纪念日必须是 2019 年 8 月 25 日。
  • 您想为闰日入职的员工做什么?
  • @CodeCrack 闰日实际上是一个很好的问题,但我认为你不会太在意。我的回答,我相信其他人只会假设周年纪念日是 2/28 而不是 2/29。这是一个 sqlfiddle,它显示了 mysql 如何将年份添加到闰日。 sqlfiddle.com/#!9/f35e91/5

标签: mysql sql


【解决方案1】:

已编辑

SELECT
  *
  ,CASE
    WHEN CURDATE() <= startDate THEN startDate + INTERVAL 3 year
    WHEN MOD((YEAR(CURDATE()) - YEAR(startDate)),3) = 0
      AND (CURDATE() - INTERVAL YEAR(CURDATE()) year) 
          <= (startDate - INTERVAL YEAR(startDate) year) 
      THEN startDate + INTERVAL (YEAR(CURDATE()) - YEAR(startDate)) year
    ELSE startDate + INTERVAL (year(CURDATE()) - YEAR(startDate) + 3 - MOD(YEAR(CURDATE()) - YEAR(startDate),3)) year
  END   as "Next3YrAniversary"
FROM
  TableName

SQL Fiddle 向您展示它如何处理测试用例: http://www.sqlfiddle.com/#!9/cf276a/1

反正思路如下:

  • startDate is Today or in future if so just add 3 years
  • Remainder of (Yesrs Diff / 3) = 0 and if current month/day is &lt;= month/day of startDate 如果今年是周年纪念日并且还没有发生,那么add number of years between current date and startDate to startDate。如果是 2016 并且周年纪念日在 2016 中,则表示方式略有不同,它将查看今年是否已经过周年纪念日。
  • 否则添加到开始日期:Number of years between Start Date &amp; Now + 3 years - Remainder of (Years between Now and startDate / 3)

关于YEAR()year 的注释。你可以写 year()YEAR() 我倾向于大写我的特定函数等,但 year() 函数只获取日期年份的 INT。所以YEAR(CURDATE()) 表示获取当前日期的年份,例如2016YEAR(startDate) 获取 startDate 的年份,例如2013MOD() 给你一个除法的余数所以2 / 3 remainder 1

2016 - 2013 gives you 3 so remainder of 3/3 is 0

不是函数的小写年份 [不带括号] 定义了可以使用日、年、月等的 INTERVAL 类型。

date INTERVAL + 1 year = add 1 year to date

另一个例子是

startDate INTERVAL + NumOfYearsDiff year = add NumOfYearsDiff to startDate

在情况 2 比较时,我所做的是通过从日期中减去年份整数来使两个日期基本上为 YEAR 0001,然后比较哪个日期会告诉我哪个日期在今年晚些时候,以了解周年纪念日是否已经过去或者今年还在继续。 TIMESTAMPDIFF 在一定程度上会自动为您执行此操作,但如果您使用,那么周年日期将是下一个日期,例如2019 如果今天是 2016。所以如果你想像今天一样展示它,你仍然需要检查我所做的相同条件,这意味着没有真正的理由使用TIMESTAMPDIFF

这里是 My Method、Spencer 和 Gordon 的 sqlfiddle 并排比较 http://www.sqlfiddle.com/#!9/cf276a/3

它显示了什么:

  • 如果今天是周年纪念日,Gordon's & Spencer 的方法会将下一个周年纪念日显示为今天 + 3 年。哪些可以或不可以?
  • Gordon 对未来日期的回答将显示 startDate 而不是 startDate + 3 年

【讨论】:

  • @CodeCrack 让我知道这是否适合您,如果您想要 5 周年纪念日、10 周年等,只需将 3s 替换为 5 或 10。
  • 我建议用三种边界情况来测试它。周年纪念日是昨天的一个(应该在昨天返回 + 3 年)。今天是周年纪念日(不知道操作员在这里想要什么)。还有一个明天是周年纪念日(应该明天返回)。
  • sqlfiddle.com/#!9/e2c3d/1 显示当周年纪念为“今年晚些时候”时,代码仍然错误地添加了3年。
  • 我推荐使用TIMESTAMPDIFF(YEAR, then, now)
  • @MatBailie 我已经开始使用 TIMESTAMPDIFF 进行开发,但是我正在从 sql-server 传输逻辑并且我得到了一个不同的和意外的值。它之间是一个 ss datediff。我相信部分是因为 TIMESTAMPDIFF 没有考虑闰年。无论如何,现在我回到电脑前,我明白了你的观点,我将展示两种解决方法
【解决方案2】:

戈登回答中的方法是一种很好的方法。我只是认为有必要稍作修改。使用方法:

INTERVAL 0 + 代替 INTERVAL 3 +

并使用

INTERVAL 3 + 代替 INTERVAL 6 +

我不想弄乱戈登的回答。

这里演示了完全相同的方法,只是语法略有不同,并且还添加了对未来 startdate 的检查,我们希望在 startdate 上添加 3 年(以获取日期第一个三周年)。

SELECT t.id
     , t.name
     , t.startdate
     , CASE
       WHEN t.startdate > DATE(NOW())
       THEN t.startdate + INTERVAL 3 YEAR
       WHEN t.startdate + INTERVAL FLOOR( TIMESTAMPDIFF(YEAR,t.startdate,NOW()) /3)*3 YEAR > DATE(NOW())
       THEN t.startdate + INTERVAL FLOOR( TIMESTAMPDIFF(YEAR,t.startdate,NOW()) /3)*3 YEAR
       ELSE t.startdate + INTERVAL FLOOR( TIMESTAMPDIFF(YEAR,t.startdate,NOW()) /3)*3 + 3 YEAR
       END AS next_3yr_anniv
  FROM mytable t
 ORDER BY ...

【讨论】:

    【解决方案3】:

    嗯,我确实认为这有点麻烦,但你可以在一个语句中做到这一点。

    我认为可行的逻辑是以年为单位的差异并四舍五入到最接近的 3 年。然后将此添加到开始日期。如果这大于当前日期,则为下一个开始日期。否则,再增加三年:

    select t.*,
           (case when startdate + interval 3 + 3 * floor((year(curdate()) - year(startdate)) / 3) year > curdate()
                 then startdate + interval 3 + 3 * floor((year(curdate()) - year(startdate)) / 3) year
                 else startdate + interval 6 + 3 * floor((year(curdate()) - year(startdate)) / 3) year
    from t;
    

    【讨论】:

    • 这种方法很接近,但鉴于今天的日期为 '2016-09-16',我认为这不适用于 startdate '2013-09-30'。从开始日期算起的下一个“三周年纪念日”将在 14 天后,即“2016-09-30”。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多