跨年度月份范围SQL查询问题:如何筛选12月、次年1-2月数据
我太懂这种坑了!用MONTH()函数直接做范围判断碰到跨年场景肯定会翻车,咱们先搞清楚为啥你的语句不行,再给你几个靠谱的解决方案。
为啥原来的语句查不到结果?
你写的MONTH(dateofit) BETWEEN 12 AND 2,本质是在找月份大于等于12且小于等于2的记录——这逻辑本身就矛盾啊,12比2大,自然啥也查不出来。哪怕改成MONTH(dateofit) BETWEEN MONTH(12) AND MONTH(1)也没用,因为MONTH(12)就是12,MONTH(1)就是1,和之前的条件完全一样,问题根本没解决。而且你完全没考虑年份的差异,12月是2017年,1、2月是2018年,光看月份肯定匹配不上。
解决方案1:用OR拆分年份+月份条件
如果是固定年份的场景(比如你例子里的2017年12月、2018年1-2月),直接把年份和月份组合起来判断最直观:
SELECT id FROM records WHERE (YEAR(dateofit) = 2017 AND MONTH(dateofit) = 12) OR (YEAR(dateofit) = 2018 AND MONTH(dateofit) IN (1, 2));
要是需要动态适配当前年份(比如每年的12月到次年2月),可以用CURRENT_DATE来自动获取年份:
SELECT id FROM records WHERE -- 当年12月 (YEAR(dateofit) = YEAR(CURRENT_DATE) AND MONTH(dateofit) = 12) -- 次年1月 OR (YEAR(dateofit) = YEAR(CURRENT_DATE) + 1 AND MONTH(dateofit) = 1) -- 次年2月 OR (YEAR(dateofit) = YEAR(CURRENT_DATE) + 1 AND MONTH(dateofit) = 2);
解决方案2:用日期范围判断(更推荐!)
上面的方法虽然能解决问题,但用YEAR()和MONTH()函数会让数据库没法用dateofit字段上的索引,数据量大的时候查询会变慢。更高效的方式是直接给日期范围:
比如固定年份的情况:
SELECT id FROM records WHERE dateofit >= '2017-12-01' AND dateofit < '2018-03-01';
这里用< '2018-03-01'而不是<= '2018-02-28',是为了避免漏掉2018-02-28当天的晚些时候的记录,同时不会包含2018-03-01的0点记录,逻辑更严谨。
如果是动态适配当前年份的场景:
SELECT id FROM records WHERE dateofit >= DATE_FORMAT(CURRENT_DATE, '%Y-12-01') AND dateofit < DATE_ADD(DATE_FORMAT(CURRENT_DATE, '%Y-12-01'), INTERVAL 3 MONTH);
DATE_FORMAT(CURRENT_DATE, '%Y-12-01')会生成当年的12月1号,加3个月就是次年的3月1号,完美覆盖你要的时间段,而且能利用索引,查询效率高很多。
内容的提问来源于stack exchange,提问作者Toleo

