mysql中应避免where month(create_time)=5,因其导致全索引扫描;正确做法是用范围查询如create_time >= '2024-05-01' and create_time
MySQL中用
MONTH()提取月份并过滤的正确写法直接在
WHERE里写MONTH(create_time) = 5能查出5月数据,但会跳过索引——除非create_time字段本身没有索引,否则这种写法会让查询变慢。真正想高效查某月,得避免对字段做函数运算。
MONTH()只返回1–12的整数,不带年份信息,所以MONTH(create_time) = 5会把所有年份的5月数据都捞出来,容易误包含历史数据- 如果表有百万级数据且
create_time建了B+树索引,用MONTH()会导致全索引扫描,而用范围查询(如BETWEEN)可走索引下推- 正确做法是构造起止时间:
create_time >= '2024-05-01' AND create_time ,既精确又高效PostgreSQL里不能直接用
MONTH()?用EXTRACT()替代PostgreSQL没有
MONTH()函数,直接写会报错:ERROR: function month(timestamp without time zone) does not exist。必须改用EXTRACT(MONTH FROM create_time),但同样存在索引失效问题。
EXTRACT(MONTH FROM create_time) = 5语法合法,但和MySQL一样无法利用create_time索引- 更推荐写法:
create_time >= '2024-05-01'::date AND create_time ,类型强制转换清晰,也支持索引- 注意时区:如果字段是
timestamptz,比较前最好显式转成对应时区,比如create_time AT TIME ZONE 'Asia/Shanghai' >= '2024-05-01'SQL Server中
DATEPART()和索引的取舍SQL Server用
DATEPART(MONTH, create_time) = 5也能跑通,但执行计划里常出现“Index Scan”而非“Index Seek”,说明没走最优路径。
DATEPART()结果不可SARGable(即无法被查询优化器用于索引查找),即使字段有索引也会降级为扫描- 若必须按月聚合统计,可考虑加计算列+索引:
ALTER TABLE orders ADD month_part AS DATEPART(MONTH, create_time),再给month_part建索引- 日常查询仍建议用范围:
create_time >= '20240501' AND create_time ,字符串格式<code>'YYYYMMDD'在SQL Server中可安全隐式转为日期跨数据库通用写法:用日期范围代替月份函数
不管用哪种数据库,只要字段类型是
DATE、DATETIME或TIMESTAMP,最稳的方案永远是手动算出当月首尾时间点。函数提取月份只是图省事,代价是性能和精度不可控。实际业务中,月份筛选往往连带年份约束,单独提月份很容易漏掉跨年场景。函数看着简单,但真要线上扛量,还是老老实实算范围靠谱。
- 别依赖
MONTH()类函数做主过滤条件,它适合做GROUP BY或SELECT输出,不适合WHERE- 动态生成范围时注意边界:用
比<code>更安全,避免毫秒/微秒截断问题- 应用层拼接SQL时,务必校验输入的年月是否合法(比如2024-13无效),否则可能查出空结果却不报错












