需用 DATE() 函数截断 DATETIME 字段的时间部分,再 GROUP BY 实现按天聚合:SELECT DATE(created_at) AS day, COUNT(*) AS order_count FROM orders WHERE created_at >= '2024-01-01' AND created_at...

用 GROUP BY date() 提取日期并聚合
MySQL 里直接对 DATETIME 字段 GROUP BY 会按秒级分组,得不到“每天”的汇总。必须先用 DATE() 函数截断时间部分,再分组:
SELECT DATE(created_at) AS day, COUNT(*) AS order_count FROM orders WHERE created_at >= '2024-01-01' AND created_at < '2024-02-01' GROUP BY DATE(created_at) ORDER BY day;
注意:这里用 created_at 而不是 <code>,避免因毫秒或时区导致漏数据。
PostgreSQL 和 SQL Server 的日期截断写法不同
PostgreSQL 没有 DATE(),得用 ::date 类型转换或 CAST(created_at AS DATE);SQL Server 推荐用 CAST(created_at AS DATE),而不是 CONVERT(DATE, created_at)(后者在某些版本下可能隐式转成字符串再转回,影响索引使用)。
- PostgreSQL:
GROUP BY created_at::date - SQL Server:
GROUP BY CAST(created_at AS DATE) - SQLite:
GROUP BY DATE(created_at)(但需确保字段是 ISO8601 格式字符串)
WHERE 条件写错会导致全表扫描
如果在 WHERE 中对日期字段用函数,比如 WHERE DATE(created_at) = '2024-01-01',数据库无法走索引(除非建了函数索引)。正确做法是让日期字段“裸露”参与比较:
- ✅
WHERE created_at >= '2024-01-01' AND created_at - ❌
WHERE DATE(created_at) = '2024-01-01' - ❌
WHERE YEAR(created_at) = 2024 AND MONTH(created_at) = 1
哪怕只是查单日,也优先用范围条件——它能命中 created_at 上的普通 B-tree 索引。
时区问题常被忽略
如果应用和数据库时区不一致,created_at 存的是 UTC,但业务要按本地时间统计,直接 DATE(created_at) 就会错。此时得先转换时区:
- MySQL:
DATE(CONVERT_TZ(created_at, '+00:00', 'Asia/Shanghai')) - PostgreSQL:
(created_at AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Shanghai')::date
别在应用层做时区转换后再传给 SQL——那样会让 WHERE 条件失去索引能力。转换必须在数据库内完成,且最好配合函数索引优化。

















