应避免直接用YEAR()和MONTH()函数筛选,因其导致索引失效;建议改用范围查询如created_at >= '2023-05-01' AND created_at < '2023-06-01'。

用 YEAR() 和 MONTH() 函数直接提取年月筛选
MySQL 和 SQL Server 支持 YEAR()、MONTH() 这类日期函数,适合快速按年份和月份切片。但要注意:它们会强制对 created_at 字段做函数计算,导致无法使用索引——如果表有千万级订单,全表扫描会明显变慢。
实操建议:
- 仅在数据量小(WHERE YEAR(created_at) = 2023 AND MONTH(created_at) = 5
- 生产环境高频查询必须避免这种写法,改用范围查询(见下一条)
- PostgreSQL 不支持
YEAR(),得用EXTRACT(YEAR FROM created_at),语法不通用
用日期范围查询替代函数,保障索引可用
真正能走索引的方式是把“2023 年 5 月”转换成两个确定的时间边界:'2023-05-01' 到 '2023-06-01'(左闭右开)。只要 created_at 字段上有索引,就能高效定位。
实操建议:
- 写成
WHERE created_at >= '2023-05-01' AND created_at - 别用
BETWEEN,它包含右边界,容易漏掉最后一秒的订单(比如'2023-05-31 23:59:59'之后的数据) - 如果字段是
DATETIME或TIMESTAMP类型,确保字符串格式带时间部分,否则 MySQL 可能隐式补零影响精度
用 DATE_FORMAT() 做年月字符串匹配(慎用)
MySQL 提供 DATE_FORMAT(created_at, '%Y-%m'),能生成类似 '2023-05' 的字符串,再用 = '2023-05' 匹配。看起来简洁,但本质仍是函数操作,同样无法利用索引。
实操建议:
- 只用于开发调试或报表导出等低频场景
- 如果硬要用,至少加个注释说明性能风险,避免被后续维护者误当标准写法
- 注意时区:如果数据库用的是 UTC 时间,而业务要求按本地时区(如东八区)归月,得先用
CONVERT_TZ()转换,否则五月第一天凌晨的数据可能被算进四月
PostgreSQL 或 SQLite 用户怎么写
PostgreSQL 没有 YEAR(),得靠 EXTRACT 或 TO_CHAR;SQLite 连原生日期函数都弱,得靠 strftime()。语法差异大,但核心逻辑不变:优先选范围查询。
实操建议:
- PostgreSQL:
WHERE created_at >= '2023-05-01' AND created_at (直接支持) - PostgreSQL(兼容旧版):
WHERE EXTRACT(YEAR FROM created_at) = 2023 AND EXTRACT(MONTH FROM created_at) = 5(不推荐) - SQLite:
WHERE strftime('%Y-%m', created_at) = '2023-05'(只能这么写,但记得建函数索引或用范围模拟)

















