应使用sum() over(partition by year(date), month(date), store_id)计算单店当月日销占比,避免子查询性能差和跨年混淆;需先to_date()截断时间戳,用nullif防除零,按业务明确是单店内占比还是全平台占比。

用 sum() over() 窗口函数按月汇总再除算占比
直接对每个店铺每天的销售额,除以它所在月份所有天的销售额总和。关键不是先 group by 月+店,而是用窗口函数动态按月切片求和——否则会丢失日粒度数据,无法回填到原行做占比计算。
常见错误是写成两层子查询:外层查日销,内层用 where date like '2024-03%' 求当月总和。这会导致每行都得重跑一次子查询,性能差,且无法泛化到跨月数据。
- 必须用
partition by year(date), month(date), store_id——注意不能只写month(date),否则不同年份的12月会被混在一起 -
date字段类型要是string或date,如果是timestamp,需先用to_date(ts_col)转换 - 如果日期字段含时分秒(如
'2024-03-15 14:22:03'),务必先截断:to_date(order_time)
SQL 示例:带格式化与空值防护
以下语句可直接运行,已处理常见边界:
SELECT
store_id,
to_date(sale_time) AS sale_date,
daily_amount,
ROUND(
daily_amount / NULLIF(
SUM(daily_amount) OVER (
PARTITION BY year(to_date(sale_time)), month(to_date(sale_time)), store_id
),
0
),
4
) AS monthly_pct
FROM (
SELECT
store_id,
sale_time,
SUM(amount) AS daily_amount
FROM sales_table
WHERE sale_time >= '2024-01-01'
GROUP BY store_id, sale_time
) t
说明:
-
NULLIF(..., 0)防止分母为 0 报错;ROUND(..., 4)控制小数位,避免浮点误差导致占比加起来不等于 1 - 外层没再
GROUP BY,是因为窗口函数已在分组内完成聚合,保留了原始日粒度行 - 如果想看某店整月占比分布,可加
ORDER BY store_id, sale_date
当月总销售额要包含所有店铺?那得改 PARTITION BY
题干说“每个店铺日销售额在当月的占比”,默认是**该店当月内部占比**(即每家店各自算自己的月度分布)。但若实际需求是“某店某日销量占**全平台当月总销**的比例”,则窗口函数的分区要改成:
- 去掉
store_id:PARTITION BY year(to_date(sale_time)), month(to_date(sale_time)) - 此时分母是当月所有店铺销售额之和,分子仍是单店单日,结果就变成“单店单日占大盘当月比重”
- 两种逻辑业务含义完全不同,确认清楚再改,别等跑完才发现口径错了
性能与分区裁剪提醒
Hive 对 year()、month() 函数不友好,很可能导致全表扫描。真正上线前必须检查执行计划:
- 把分区字段(如
dt STRING)作为物理分区列,并在WHERE中显式过滤:WHERE dt BETWEEN '2024-03-01' AND '2024-03-31' - 避免在
PARTITION BY中用函数包裹分区列,例如不要写PARTITION BY year(dt),而应提前在源表或中间层生成year_month STRING列并分区 - 如果数据量大且仅需近三个月,用分区裁剪比函数计算高效得多
窗口函数本身没问题,但分母计算范围一旦失控,一个 sum() over() 就可能拖垮整个作业。别只盯着 SQL 写对了,得盯住它实际扫了多少数据。










