sqlite中唯一可靠的日期分组工具是strftime(),因其不支持year()、month()等专用函数,必须用strftime('%y-%m', date_column)提取年月并group by,且需确保日期格式合法、排除null值。

SQLite 的 strftime() 是唯一靠谱的日期分组工具
SQLite 没有 YEAR()、MONTH() 这类专用函数,所有日期提取都得靠 strftime()。它用格式化字符串从日期字段中“抠”出年月信息,是统计每月数据的唯一可行路径。
常见错误是直接写 GROUP BY month(created_at) —— SQLite 会报错 no such function: month。必须用 strftime('%Y-%m', created_at) 替代。
注意:created_at 字段值必须是合法的 SQLite 日期格式(如 '2024-03-15 14:22:08'、'2024-03-15' 或 Unix 时间戳),否则 strftime() 返回 NULL,导致该行被排除在分组外。
按年月分组统计的典型 SQL 写法
核心就是把 strftime('%Y-%m', date_column) 当作一个虚拟列来 GROUP BY,再配合聚合函数:
SELECT
strftime('%Y-%m', order_date) AS ym,
COUNT(*) AS cnt,
SUM(amount) AS total
FROM orders
WHERE order_date IS NOT NULL
GROUP BY strftime('%Y-%m', order_date)
ORDER BY ym;
几个关键点:
-
'%Y-%m'输出形如'2024-03',适合按自然月排序;换成'%Y%m'(如'202403')也能用,但字符串排序和数值排序结果一致 - 务必加
WHERE date_column IS NOT NULL,避免strftime()对空值返回NULL后,整组被吞掉 - 如果原始日期存的是秒级 Unix 时间戳(整数),要先转成文本:
strftime('%Y-%m', datetime(timestamp_col, 'unixepoch'))
处理跨年、补全缺失月份要用 LEFT JOIN + 生成序列
纯 GROUP BY strftime() 只返回有数据的月份。若想补全 2024 年全部 12 个月(含零记录),SQLite 本身不支持 GENERATE_SERIES,得手动生成月份列表。
宝塔面板11.3.0是一款针对Linux服务器设计的可视化管理工具,通过重构核心模块实现资源占用显著降低,尤其适合低配置服务器环境。它将复杂的命令行操作转化为直观的图形界面,帮助开发者快速完成网站部署、环境配置及日常运维工作,无需专业技术背景即可高效管理服务器。
最轻量的做法是用 WITH RECURSIVE 构造月份序列,再 LEFT JOIN 原表:
WITH months AS (
SELECT '2024-01' AS ym
UNION ALL
SELECT strftime('%Y-%m', ym || '-01', '+1 month')
FROM months
WHERE ym
<p>这个方案的坑:</p>
- 递归深度默认上限是 1000,覆盖多年份需显式设置:
PRAGMA recursive_triggers = ON;(不影响此例) -
strftime(..., '+1 month')在月底(如'2024-01-31')可能跳到下下月,但用于构造'YYYY-MM'序列时影响不大 - 硬编码起止月份不灵活,真实项目建议用应用层生成或视图封装
性能与索引注意事项
strftime() 表达式无法直接走索引 —— 即便 order_date 上建了索引,GROUP BY strftime('%Y-%m', order_date) 仍会全表扫描。
优化办法只有两个:
- 加一个生成列(SQLite 3.31.0+):
ALTER TABLE orders ADD COLUMN ym TEXT GENERATED ALWAYS AS (strftime('%Y-%m', order_date)) STORED;,再对ym建索引 - 或冗余存储年月字段(如
order_ym TEXT CHECK(length(order_ym)=7)),写入时由应用保证一致性,然后直接GROUP BY order_ym
别指望函数索引(CREATE INDEX idx_ym ON orders(strftime('%Y-%m', order_date)))—— SQLite 目前不支持基于函数表达式的索引。










