库龄统计必须确保入库时间字段为日期类型,否则DATEDIFF结果异常;需用STR_TO_DATE显式转换、CASE WHEN分段统计、区分COUNT(*)与COUNT(DISTINCT sku_id),并校准时区。

库龄字段必须是日期类型,否则 DATEDIFF 会返回意外结果
很多库存表里“入库时间”存的是字符串(如 '2023-05-12' 或更糟的 '2023/05/12 14:30'),直接用 DATEDIFF(CURDATE(), entry_time) 可能返回 NULL 或 0——MySQL 会尝试隐式转换,但遇到格式不统一就失效。
实操建议:
- 先确认字段类型:
DESCRIBE inventory;,看entry_time是不是DATE或DATETIME - 如果不是,用
STR_TO_DATE(entry_time, '%Y-%m-%d')显式转(注意格式符要和实际数据严格匹配) - 临时测试转换是否成功:
SELECT entry_time, STR_TO_DATE(entry_time, '%Y-%m-%d') AS parsed FROM inventory LIMIT 5;
用 CASE WHEN + GROUP BY 划分库龄区间最灵活
业务上常需要按“0–30天、31–90天、91–180天、180+天”统计 SKU 数量,不能依赖固定函数——HISTOGRAM 在 MySQL 8.0+ 才有且不支持自定义分桶逻辑。
实操建议:
- 用嵌套
CASE WHEN计算每个 SKU 所属库龄段,再外层GROUP BY汇总 - 避免在
WHERE里过滤库龄(比如只查 >90 天),否则会漏掉其他区间的计数 - 示例语句:
SELECT
CASE
WHEN DATEDIFF(CURDATE(), entry_time) BETWEEN 0 AND 30 THEN '0-30天'
WHEN DATEDIFF(CURDATE(), entry_time) BETWEEN 31 AND 90 THEN '31-90天'
WHEN DATEDIFF(CURDATE(), entry_time) BETWEEN 91 AND 180 THEN '91-180天'
ELSE '180+天'
END AS age_group,
COUNT(*) AS sku_count
FROM inventory
WHERE entry_time IS NOT NULL
GROUP BY age_group
ORDER BY FIELD(age_group, '0-30天', '31-90天', '91-180天', '180+天');
COUNT(DISTINCT sku_id) 和 COUNT(*) 含义完全不同
一个 SKU 可能在不同仓库有多条记录(比如 sku_id='A123' 在北京仓、上海仓各有一条),这时 COUNT(*) 统计的是库存记录条数,COUNT(DISTINCT sku_id) 才是真正有多少个 SKU 卡在该库龄段。
实操建议:
- 明确业务口径:是“有多少SKU积压”,还是“积压了多少库存单位”?前者用
COUNT(DISTINCT sku_id),后者用COUNT(*)或SUM(qty) - 加
DISTINCT会略慢,但对千万级以下数据影响不大;若性能敏感,可提前建(sku_id, entry_time)联合索引 - 别漏掉
WHERE sku_id IS NOT NULL,避免空值干扰去重结果
时区与 CURDATE() 的一致性容易被忽略
如果数据库服务器时区是 UTC,而业务要求按本地(比如东八区)日期算库龄,CURDATE() 返回的就是 UTC 当日,会导致所有库龄少算 8 小时——对刚入库几小时的 SKU,可能从“0天”变成“-1天”(datediff 返回负数)。
实操建议:
- 查当前会话时区:
SELECT @@time_zone;,确保和业务期望一致 - 若需强制本地时间,改用
CURDATE() + INTERVAL 8 HOUR(仅当服务器为 UTC 时) - 更稳妥的做法是把“计算基准日”作为参数传入(例如用应用层传
'2024-06-01'),避免依赖服务端时钟
COUNT 和 CASE 写得再标准也没用。

















