mysql用year()/month()/day()提取日期分组返回数字,postgresql需用extract();按年月日字符串分组应使用date_format()或to_char();where中避免对时间字段用函数以免索引失效。

MySQL中用YEAR()、MONTH()、DAY()直接提取时间字段分组
如果你的订单表里有个created_at(DATETIME或DATE类型),想按年/月/日统计数量,最直白的做法就是用内置日期函数提取部分值再GROUP BY。注意:这些函数返回的是数字,不是字符串,所以YEAR('2023-05-12')结果是2023,不是'2023'——这点在做跨年对比或排序时容易出错。
常见写法:
SELECT YEAR(created_at) AS year, COUNT(*) AS cnt FROM orders GROUP BY YEAR(created_at);
同理可换为MONTH(created_at)或DAY(created_at)。但要注意:DAY()只取日份(1–31),不带年月信息,单独用会把不同月份的1号全混在一起,基本没业务意义。
PostgreSQL要用EXTRACT()而不是YEAR()
PostgreSQL不支持YEAR()这种简写函数,必须用EXTRACT(YEAR FROM created_at)。语法更啰嗦,但好处是统一——年、月、日、小时都用同一套EXTRACT逻辑,不容易记混。
示例:
SELECT EXTRACT(YEAR FROM created_at) AS year,
EXTRACT(MONTH FROM created_at) AS month,
COUNT(*) AS cnt
FROM orders
GROUP BY 1, 2;
这里用GROUP BY 1, 2代替列名,避免重复写EXTRACT表达式;但要注意,如果后续加了ORDER BY,列序号引用要同步调整。
容易踩的坑:EXTRACT(DAY FROM ...)返回的是整数(如12),不是补零格式('12'或'012'),拼接成“YYYY-MM-DD”需要手动TO_CHAR(created_at, 'YYYY-MM-DD')。
用DATE_FORMAT()(MySQL)或TO_CHAR()(PostgreSQL)生成标准日期字符串再分组
当你要按“2023-05”(年月)或“2023-05-12”(年月日)这种可读格式聚合,且后续可能导出或展示时,别硬拼YEAR()+MONTH(),直接用格式化函数更稳。
- MySQL:
DATE_FORMAT(created_at, '%Y-%m')→'2023-05';DATE_FORMAT(created_at, '%Y-%m-%d')→'2023-05-12' - PostgreSQL:
TO_CHAR(created_at, 'YYYY-MM')或TO_CHAR(created_at, 'YYYY-MM-DD')
这样生成的字符串天然可排序、可读性强,也方便和前端传来的日期范围字符串直接比对。但注意:字符串分组比数字分组略慢,数据量超百万时差异开始明显。
WHERE条件必须放在GROUP BY之前,且慎用函数包裹索引字段
很多人会写WHERE YEAR(created_at) = 2023,这会导致created_at字段上的索引失效——因为对字段施加函数后,数据库无法走B-tree索引的范围扫描。
正确做法是用范围查询:
WHERE created_at >= '2023-01-01' AND created_at <p>同样适用于月、日粒度:</p>
- 查2023年5月:
created_at >= '2023-05-01' AND created_at - 查某天:
created_at >= '2023-05-12' AND created_at
这个习惯比“怎么分组”更重要——没加索引优化的分组语句,在大表上跑一次可能就卡住。











