date_format本身不慢,慢在使索引失效并强制逐行计算;where中使用会导致全表扫描,应改用范围查询;select中使用仅影响结果集格式化性能,建议交由应用层处理。

DATE_FORMAT 本身不慢,慢在它让索引失效、强制逐行计算——尤其在 WHERE 或 GROUP BY 中使用时,查询可能从毫秒级退化到秒级甚至更久。
WHERE 条件里用 DATE_FORMAT 必然全表扫描
比如写成 WHERE DATE_FORMAT(crt_date, '%Y-%m-%d') = '2023-01-01',MySQL 无法利用 crt_date 上的索引,因为索引存的是原始时间值,不是格式化后的字符串。
- 正确做法是把函数从字段上拿掉,改用范围比较:
WHERE crt_date >= '2023-01-01' AND crt_date - 如果要查“最近一小时”,别写
DATE_FORMAT(NOW(), '%Y-%m-%d %H')去匹配,改用:WHERE crt_date >= DATE_SUB(NOW(), INTERVAL 1 HOUR) - 已有索引但没被命中?用
EXPLAIN看key列是否为NULL,是就说明函数导致索引失效
SELECT 中用 DATE_FORMAT 不影响索引,但会拖慢大结果集
如果只是 SELECT DATE_FORMAT(order_date, '%Y年%m月'),且 WHERE 条件已走索引,那性能损耗主要来自每行一次字符串构造。数据量越大,CPU 和内存压力越明显。
- 百万行以上结果时,建议只查原始
DATETIME字段,把格式化交给应用层(PHP 的date()、Python 的strftime()、Java 的DateTimeFormatter都比 MySQL 快) - 若必须在 SQL 层输出格式化值,且只取年月日,可用更轻量组合:
CONCAT(YEAR(d), '-', LPAD(MONTH(d), 2, '0'), '-', LPAD(DAY(d), 2, '0')),比DATE_FORMAT少解析 format 字符串 - 避免在
ORDER BY或GROUP BY中用它,否则排序/分组都失去索引加速能力
高频格式化需求可提前物化,别临时算
如果业务反复需要按 “年-月” 统计、且该维度不变,硬扛每次查询计算不现实。
- 加一个生成列:
ALTER TABLE orders ADD COLUMN ym CHAR(7) STORED AS (DATE_FORMAT(order_date, '%Y-%m')),再给它建索引 - 或维护一张轻量汇总表,用定时任务(如每天凌晨)跑:
INSERT INTO orders_monthly (ym, cnt) SELECT DATE_FORMAT(order_date, '%Y-%m'), COUNT(*) FROM orders GROUP BY 1 - 临时表方案慎用:
CREATE TEMPORARY TABLE虽能缓存结果,但每次连接新建,对高并发无实质帮助
真正卡住性能的从来不是 DATE_FORMAT 这个函数本身,而是它出现在不该出现的位置——尤其是 WHERE 子句里包裹日期字段。只要挪开它,让原始时间值直接参与比较,90% 的“慢”就消失了。











