SQL查询早于指定年月的记录:现有语句失效,求最优实现方案
你遇到的问题其实是逻辑条件的覆盖不全——原语句year <= @Year AND month < @Month只考虑了两种情况:和指定年份相同但月份更早,以及年份更早但月份必须小于指定月份。但像2014年11月这种「年份早于指定年份但月份更大」的记录,就被错误过滤了,因为11不小于8,不符合month < @Month的条件。
下面是几种最优的实现方式,你可以根据自己的数据库环境和需求选择:
1. 拆分逻辑条件(最直观易维护)
直接把条件拆成两种合法情况:要么年份小于指定年份(此时不管月份是多少都符合要求),要么年份等于指定年份且月份更早。用OR连接即可:
SELECT * FROM testTable WHERE year < @Year OR (year = @Year AND month < @Month)
这种写法逻辑清晰,几乎所有数据库都支持,而且如果year和month有复合索引的话,查询性能会很好,后期维护也容易理解。
2. 转换为日期值比较(可读性最强)
把Year和Month字段拼接成一个具体的日期(比如每月的第一天),然后和指定年月的起始日期做比较:
-- SQL Server 2012+ 可用DATEFROMPARTS SELECT * FROM testTable WHERE DATEFROMPARTS(year, month, 1) < DATEFROMPARTS(@Year, @Month, 1) -- MySQL 可替代写法 SELECT * FROM testTable WHERE STR_TO_DATE(CONCAT(year, '-', month, '-01'), '%Y-%m-%d') < STR_TO_DATE(CONCAT(@Year, '-', @Month, '-01'), '%Y-%m-%d')
这种写法把两个字段的比较转化为单一日期的比较,可读性极强,不需要额外理解复杂的逻辑拆分,数据库对日期比较的优化也很成熟。
3. 数值化年月比较(性能最优)
把年月转换成一个整数(比如2017年8月就是201708),直接做数值大小比较:
SELECT * FROM testTable WHERE (year * 100 + month) < (@Year * 100 + @Month)
这种方式是纯数值计算,不需要调用日期函数,性能非常出色,适合数据量极大、对查询速度要求很高的场景,逻辑也很直接,一眼就能明白是在比较年月的先后顺序。
小提示
如果你的表数据量较大,建议给year和month建立复合索引;如果使用日期转换的方式,可以考虑创建计算列索引(比如基于DATEFROMPARTS(year, month, 1)的索引),进一步提升查询效率。
内容的提问来源于stack exchange,提问作者Nerd in Training

