需用 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 <p>注意:这里用 <code>created_at 而不是 <code>,避免因毫秒或时区导致漏数据。</code></code></p> <h3>PostgreSQL 和 SQL Server 的日期截断写法不同</h3> <p>PostgreSQL 没有 <code>DATE()</code>,得用 <code>::date</code> 类型转换或 <code>CAST(created_at AS DATE)</code>;SQL Server 推荐用 <code>CAST(created_at AS DATE)</code>,而不是 <code>CONVERT(DATE, created_at)</code>(后者在某些版本下可能隐式转成字符串再转回,影响索引使用)。</p>
- 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 条件失去索引能力。转换必须在数据库内完成,且最好配合函数索引优化。











