应优先用DATE(create_time)等索引友好函数分组,避免DATE_FORMAT导致索引失效;细粒度分组需注意格式一致性与时区对齐;补全缺失日期须主动JOIN序列而非依赖GROUP BY。

MySQL用DATE_FORMAT按日/小时/分钟分组,但别直接拼字符串做分组键
直接写 GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d') 能跑通,但隐患大:如果字段没索引,或 WHERE 条件里用了函数(比如 WHERE DATE_FORMAT(create_time, '%Y-%m') = '2026-04'),索引会完全失效,查大表直接变慢查询。
更稳妥的做法是先截日期再分组:GROUP BY DATE(create_time),它能走 create_time 上的索引(前提是该字段有索引且类型是 DATETIME/TIMESTAMP)。
按小时或15分钟这类细粒度分组时,DATE_FORMAT 仍可用,但要注意格式一致性:
- 按小时:用
'%Y-%m-%d %H:00:00',不是'%Y-%m-%d %H'——后者在 ORDER BY 时可能因字符串排序错乱(比如 '2026-04-01 10' 排在 '2026-04-01 9' 后面) - 按15分钟:不能靠
DATE_FORMAT直接切,得用数学运算:FLOOR(UNIX_TIMESTAMP(create_time) / 900),再配合FROM_UNIXTIME(... * 900)还原起始时间点
PostgreSQL用TRUNC或date_trunc,注意时区对齐问题
TRUNC(create_time) 在 PostgreSQL 中不生效——那是 Oracle 的写法。PostgreSQL 正确做法是 date_trunc('day', create_time) 或更简洁的 create_time::date。
但真实业务中,时间字段常存为 UTC,而报表要按本地时间(如 'Asia/Shanghai')统计。若直接 date_trunc('day', create_time),跨零点的数据会被分到错误日期(比如 UTC 2026-04-16 16:00 对应北京时间 2026-04-17 00:00)。
必须显式转换时区:
- 正确:
date_trunc('day', create_time AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Shanghai') - 错误:
date_trunc('day', create_time AT TIME ZONE 'Asia/Shanghai')(没先转 UTC,原始时区不明时结果不可控)
SQL Server和Oracle不支持DATE_FORMAT,别抄MySQL写法
SQL Server 没有 DATE_FORMAT 函数,用 CONVERT(VARCHAR, create_time, 120) 或 FORMAT(create_time, 'yyyy-MM-dd') 只能用于显示,不能用于高效分组——它们同样导致索引失效。
推荐用类型转换:
- 按日分组:
CAST(create_time AS DATE)(SQL Server 2008+)或CONVERT(DATE, create_time) - 按月分组:必须同时用
YEAR(create_time)和MONTH(create_time),只用DATEPART(MONTH, create_time)会跨年混在一起(比如 2025 年 12 月和 2026 年 1 月都算作 “1”)
Oracle 同理:TRUNC(create_time) 是对的,但 TO_CHAR(create_time, 'YYYY-MM-DD') 是错的——它生成字符串,无法利用索引,且排序逻辑依赖字符序而非时间序。
补全缺失日期不能靠GROUP BY,得主动LEFT JOIN序列
GROUP BY 天然跳过没数据的日期,但图表、BI 工具需要连续横轴。指望数据库自动填 0 是徒劳的。
不同数据库补日期方式差异大:
- PostgreSQL:
GENERATE_SERIES('2026-04-01'::DATE, '2026-04-15'::DATE, '1 day')生成日期列,再LEFT JOIN业务表 - MySQL 8.0+:用递归 CTE 构造日期序列,旧版只能靠 UNION ALL 拼或预建数字表
- SQL Server:用
master..spt_values或 CTE +ROW_NUMBER()生成整数序列,再转为日期
关键点:补零必须用 COALESCE(COUNT(t.id), 0),不能写 COUNT(*)——后者会把 LEFT JOIN 的 NULL 行也计为 1。










