業務常見需求:「查詢未來 N 天內過生日的同事」。核心難點在於**跨年邊界**:12 月底查詢時,1 月初的生日屬於「明年」,直接用今年年份構造日期會導致 DATEDIFF 為負數,查漏數據。本文給出通用雙分支解法並完整驗證。
DATE(如 1992-01-01),無年份業務含義。DATEDIFF(date1, date2) 返回 date1 - date2 天數,未來 N 天含今日 = BETWEEN 0 AND N-1。SELECT * FROM ali_users WHERE
-- 分支 1:今年生日在未來 5 天內(含今日)
DATEDIFF(
CAST(CONCAT(YEAR(NOW()), DATE_FORMAT(birthday, '-%m-%d')) AS DATE),
CAST(DATE_FORMAT(NOW(), '%Y-%m-%d') AS DATE)
) BETWEEN 0 AND 4
OR
-- 分支 2:明年生日在未來 5 天內(解決跨年)
DATEDIFF(
CAST(CONCAT(YEAR(NOW())+1, DATE_FORMAT(birthday, '-%m-%d')) AS DATE),
CAST(DATE_FORMAT(NOW(), '%Y-%m-%d') AS DATE)
) BETWEEN 0 AND 4;
DATE_FORMAT(birthday, '-%m-%d'):提取月日,拼接為 -MM-DD。CONCAT(YEAR(NOW()), ...):構造「今年生日完整日期」。CAST(... AS DATE):字符串轉日期類型,供 DATEDIFF 使用。YEAR(NOW())+1:構造「明年生日」,覆蓋 12 月底查 1 月初場景。CREATE TABLE ali_users (
username VARCHAR(10), birthday DATE NOT NULL, iphone VARCHAR(16)
);
INSERT INTO ali_users VALUES
('MaoYi','1985-09-04','13998786543'), ('LiuEr','1985-08-30','13998786543'),
('ZhangSan','1981-01-01','13998786543'), ('LiSi','1983-01-02','13998786543'),
('WangWu','1984-11-01','13998786543'), ('ZhaoLiu','1984-11-01','13998786543'),
('SongQi','1986-08-31','13998786543'), ('HuangBa','1989-09-01','13998786543'),
('ZengJiu','1989-09-02','13998786543'), ('LuoShi','1985-09-03','13998786543'),
('Tom','1995-09-05','13998786543'), ('Licy','1991-12-30','13998286543'),
('Cari','1992-12-31','13998286543'), ('Mark','1992-01-03','13998286543'),
('Ruby','1992-01-04','13998286547');
-- 直接用 NOW() 執行 -- 結果 5 條:LiuEr(8/30)、SongQi(8/31)、HuangBa(9/1)、ZengJiu(9/2)、LuoShi(9/3) -- 驗證:8/30~9/3 共 5 天,正確
-- 應用層生成:['08-30','08-31','09-01','09-02','09-03']
SELECT * FROM ali_users
WHERE DATE_FORMAT(birthday, '%m-%d') IN ('08-30','08-31','09-01','09-02','09-03');
birthday 列索引(需建函數索引或冗余 birthday_md CHAR(5) 列)。SET @days = 5; -- 可改為 7、30 等 SELECT * FROM ali_users WHERE DATEDIFF(CAST(CONCAT(YEAR(NOW()), DATE_FORMAT(birthday, '-%m-%d')) AS DATE), CAST(DATE_FORMAT(NOW(), '%Y-%m-%d') AS DATE)) BETWEEN 0 AND @days-1 OR DATEDIFF(CAST(CONCAT(YEAR(NOW())+1, DATE_FORMAT(birthday, '-%m-%d')) AS DATE), CAST(DATE_FORMAT(NOW(), '%Y-%m-%d') AS DATE)) BETWEEN 0 AND @days-1;
**「雙分支:今年 + 明年」是解決生日跨年查詢的通用模式**。核心在於構造兩個候選日期分別比較,邏輯清晰、兼容性強(MySQL 5.1+ 均可用)。大表生產環境建議**應用層預計算日期集合 + 索引優化**,將計算下推、避免全表掃描。