[SQL] 生日问题的一种解法

这是同事做分享的时候提及的一个问题, 觉得比较有意思就尝试了一下

生日问题

用一条 SQL 得出雇员们的全名,以及他的最近一次生日(如果他今年没有过生日,显示今年的生日,如果今年过生日了,显示明年的生日), 环境为 MySQL 5.7+,

表结构如下:

CREATE TABLE employees (
    emp_no      INT             NOT NULL,
    birth_date  DATE            NOT NULL,
    first_name  VARCHAR(14)     NOT NULL,
    last_name   VARCHAR(16)     NOT NULL,
    gender      ENUM ('M','F')  NOT NULL,    
    hire_date   DATE            NOT NULL,
    PRIMARY KEY (emp_no)
);

记录详见附录

废话少说, 上代码

虽然看着 实际很简单, 都是注释害的

-- 为了思路清晰, 分了很多子查询, 对单测也友好
-- 思路真的很简单, 别掉队
-- PS: 不考虑时区问题
-- PS: 不考虑跨天执行的临界情况
-- PS: 部分字段使用驼峰命名, 莫怪, 真的只是为了更清晰一些

SELECT fullname, birthday, 
    -- 这里就算正式开始了
    CASE 
      -- 如果生日是 02-29 直接取下一个 02-29 就可以了
      WHEN birthMonthDay = 229 THEN str_to_date(next_229, "%Y%m%d")
      -- 否则, 只需要看是不是需要 +1 年: 今年的生日已经过了 +1, 否则不加
      ELSE str_to_date((thisYear + if(birthMonthDay < thisMonthDay, 1, 0)) * 10000 + birthMonthDay, "%Y%m%d")
    END as next_birthday
    -- 到这里整体思路就已经结束了, 意外不? 简单吧? 还没完, 咱们还有几个问题没解决呢
FROM 
-- 员工的基础信息拆解: 为了简化上面的操作, 非常简单, 不多解释
  (
    SELECT 
      CONCAT(first_name, ' ', last_name) as fullname, birth_date as birthday, 
      0 + date_format(birth_date, '%m%d') as birthMonthDay
    FROM employees
  ) t_employees_meta
-- 接下来, 最关键的就是解决 next_229, 即: 下一个 02-29 在哪一年?
JOIN (
  -- 如果可以的话, 拆成视图: CREATE VIEW t_today_meta AS
  SELECT *, 
    -- 解决 next_229 问题:
    CASE 
      -- 如果今天是 02-29 肯定就是今天了 (可以合并到下一条, 为了简单先剔出来)
      WHEN thisMonthDay = 229 THEN thisYear * 10000 + 229
      -- 如果今天小于 02-29, 且是闰年: 直接取今年的 02-29
      WHEN thisMonthDay < 229 and (thisYear % 4 = 0 AND thisYear % 100 <> 0 OR thisYear % 400 = 0)
        THEN thisYear * 10000 + 229
      -- 否则为下一个闰年的 02-29
      -- 如果下一个疑似闰年不是闰年, +4 后必然是闰年
      --   PS: 不需要判断 %4, 因为候选已经满足了
      ELSE if(nextLeapYearCandidate % 100 <> 0 OR nextLeapYearCandidate % 400 = 0,
        nextLeapYearCandidate, nextLeapYearCandidate +4) * 10000 + 229
    END as next_229
  FROM (
    SELECT
      today,
      year(today) as thisYear,
      0 + date_format(today, '%m%d') as thisMonthDay,
      -- 下一个疑似闰年, 只满足 4 的倍数, 先不管其他 (为了上层更简单, 这里先计算好这个候选)
      year(today) + 4 - year(today) % 4 as nextLeapYearCandidate
    FROM (
      -- 为了方便测试, 写成子查询, 可以指定某天
      SELECT curdate()
      -- SELECT str_to_date(20181101, "%Y%m%d")
      -- SELECT str_to_date(20200228, "%Y%m%d")
      -- SELECT str_to_date(20200229, "%Y%m%d")
      -- SELECT str_to_date(20980220, "%Y%m%d")
      -- SELECT str_to_date(20980301, "%Y%m%d")
      -- SELECT str_to_date(19970301, "%Y%m%d")
      -- SELECT str_to_date(20000229, "%Y%m%d")
        as today
    ) t_today_source
  ) t_today
) t_today_meta

执行结果详见附录

其他思考

  • 优化点: 将解决 next_229 问题 的部分拆成视图 (VIEW), 可以实现共用, 题目要求 "用一条 SQL" 所以就没这么做

附录:

员工数据:

INSERT INTO `employees` VALUES (10001,'1953-02-28','Georgi','Facello','M','1986-06-26'),
(10002,'1964-02-29','Bezalel','Simmel','F','1985-11-21'),
(10003,'1959-12-03','Parto','Bamford','M','1986-08-28'),
(10004,'1954-05-01','Chirstian','Koblick','M','1986-12-01'),
(10005,'1955-01-21','Kyoichi','Maliniak','M','1989-09-12'),
(10006,'1953-11-20','Anneke','Preusig','F','1989-06-02'),
(10007,'1957-05-23','Tzvetan','Zielinski','F','1989-02-10');

数据来源: https://github.com/datacharmer/test_db, 有稍许修改

查询结果

  • 日期: 20181101
+-------------------+------------+---------------+
| fullname          | birthday   | next_birthday |
+-------------------+------------+---------------+
| Georgi Facello    | 1953-02-28 | 2019-02-28    |
| Bezalel Simmel    | 1964-02-29 | 2020-02-29    |
| Parto Bamford     | 1959-12-03 | 2018-12-03    |
| Chirstian Koblick | 1954-05-01 | 2019-05-01    |
| Kyoichi Maliniak  | 1955-01-21 | 2019-01-21    |
| Anneke Preusig    | 1953-11-20 | 2018-11-20    |
| Tzvetan Zielinski | 1957-05-23 | 2019-05-23    |
+-------------------+------------+---------------+
  • 日期: 20200228
+-------------------+------------+---------------+
| fullname          | birthday   | next_birthday |
+-------------------+------------+---------------+
| Georgi Facello    | 1953-02-28 | 2020-02-28    |
| Bezalel Simmel    | 1964-02-29 | 2020-02-29    |
| Parto Bamford     | 1959-12-03 | 2020-12-03    |
| Chirstian Koblick | 1954-05-01 | 2020-05-01    |
| Kyoichi Maliniak  | 1955-01-21 | 2021-01-21    |
| Anneke Preusig    | 1953-11-20 | 2020-11-20    |
| Tzvetan Zielinski | 1957-05-23 | 2020-05-23    |
+-------------------+------------+---------------+
  • 日期: 20200229
+-------------------+------------+---------------+
| fullname          | birthday   | next_birthday |
+-------------------+------------+---------------+
| Georgi Facello    | 1953-02-28 | 2021-02-28    |
| Bezalel Simmel    | 1964-02-29 | 2020-02-29    |
| Parto Bamford     | 1959-12-03 | 2020-12-03    |
| Chirstian Koblick | 1954-05-01 | 2020-05-01    |
| Kyoichi Maliniak  | 1955-01-21 | 2021-01-21    |
| Anneke Preusig    | 1953-11-20 | 2020-11-20    |
| Tzvetan Zielinski | 1957-05-23 | 2020-05-23    |
+-------------------+------------+---------------+
  • 日期: 20980220

取这个 test case 是因为 2100 年不是闰年, 看是否真的跳过了

+-------------------+------------+---------------+
| fullname          | birthday   | next_birthday |
+-------------------+------------+---------------+
| Georgi Facello    | 1953-02-28 | 2098-02-28    |
| Bezalel Simmel    | 1964-02-29 | 2104-02-29    |
| Parto Bamford     | 1959-12-03 | 2098-12-03    |
| Chirstian Koblick | 1954-05-01 | 2098-05-01    |
| Kyoichi Maliniak  | 1955-01-21 | 2099-01-21    |
| Anneke Preusig    | 1953-11-20 | 2098-11-20    |
| Tzvetan Zielinski | 1957-05-23 | 2098-05-23    |
+-------------------+------------+---------------+
  • 日期: 20980301
+-------------------+------------+---------------+
| fullname          | birthday   | next_birthday |
+-------------------+------------+---------------+
| Georgi Facello    | 1953-02-28 | 2099-02-28    |
| Bezalel Simmel    | 1964-02-29 | 2104-02-29    |
| Parto Bamford     | 1959-12-03 | 2098-12-03    |
| Chirstian Koblick | 1954-05-01 | 2098-05-01    |
| Kyoichi Maliniak  | 1955-01-21 | 2099-01-21    |
| Anneke Preusig    | 1953-11-20 | 2098-11-20    |
| Tzvetan Zielinski | 1957-05-23 | 2098-05-23    |
+-------------------+------------+---------------+
  • 日期: 19970301
+-------------------+------------+---------------+
| fullname          | birthday   | next_birthday |
+-------------------+------------+---------------+
| Georgi Facello    | 1953-02-28 | 1998-02-28    |
| Bezalel Simmel    | 1964-02-29 | 2000-02-29    |
| Parto Bamford     | 1959-12-03 | 1997-12-03    |
| Chirstian Koblick | 1954-05-01 | 1997-05-01    |
| Kyoichi Maliniak  | 1955-01-21 | 1998-01-21    |
| Anneke Preusig    | 1953-11-20 | 1997-11-20    |
| Tzvetan Zielinski | 1957-05-23 | 1997-05-23    |
+-------------------+------------+---------------+
  • 日期: 20000229
+-------------------+------------+---------------+
| fullname          | birthday   | next_birthday |
+-------------------+------------+---------------+
| Georgi Facello    | 1953-02-28 | 2001-02-28    |
| Bezalel Simmel    | 1964-02-29 | 2000-02-29    |
| Parto Bamford     | 1959-12-03 | 2000-12-03    |
| Chirstian Koblick | 1954-05-01 | 2000-05-01    |
| Kyoichi Maliniak  | 1955-01-21 | 2001-01-21    |
| Anneke Preusig    | 1953-11-20 | 2000-11-20    |
| Tzvetan Zielinski | 1957-05-23 | 2000-05-23    |
+-------------------+------------+---------------+
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • 关于Mongodb的全面总结 MongoDB的内部构造《MongoDB The Definitive Guide》...
    中v中阅读 31,928评论 2 89
  • 天气越来越暖和,你还在戴着大围巾,大手套,大口罩吗?还不赶紧换掉!换季,当然得时尚换装备,必备的当然是fashio...
    迪儿的神奇魔力屋阅读 287评论 0 1
  • 我在县城C通往城市F的大巴车上。 车内有35个位子,今天这班车有三个人没去售票处买票。在大巴上买票,司机赚了24块...
    小拾晨阅读 273评论 0 1
  • 书籍:富爸爸财务自由之路 字数:800 “这个月怎么又用了这么多的钱?明明都没买什么东西呀?”“信用卡又要还了,怎...
    睿迪格阅读 375评论 0 0
  • 因为遇见好多人,所以走丢好多人。
    yqg2227694_73d4阅读 144评论 0 0