with month_gmv as ( select date_trunc('month', order_date) as month_dt, sum(amount) as gmv from orders group by 1 ) select * from month_gmv order by month_dt;
这一点特别重要:先聚合到目标粒度,再做同比、环比、累计。
三、同比 SQL 怎么写
写法一:lelf join 去年同期
这是最稳的一种写法。
with month_gmv as ( select date_trunc('month', order_date) as month_dt, sum(amount) as gmv from orders group by 1 ) select a.month_dt, a.gmv as curr_gmv, b.gmv as last_year_gmv, a.gmv - b.gmv as yoy_diff, case when b.gmv is null or b.gmv = 0 then null else (a.gmv - b.gmv) / b.gmv end as yoy_rate from month_gmv a left join month_gmv b on a.month_dt = b.month_dt + interval '1 year' order by a.month_dt;
同比增长率公式就是:
本期减去年同期,再除以去年同期。
写法二:用 lag 取去年同月
如果你的时间序列是连续的,也可以直接用窗口函数。
with month_gmv as ( select date_trunc('month', order_date) as month_dt, sum(amount) as gmv from orders group by 1 ) select month_dt, gmv, lag(gmv, 12) over(order by month_dt) as last_year_gmv, case when lag(gmv, 12) over(order by month_dt) is null or lag(gmv, 12) over(order by month_dt) = 0 then null else (gmv - lag(gmv, 12) over(order by month_dt)) / lag(gmv, 12) over(order by month_dt) end as yoy_rate from month_gmv order by month_dt;
如果中间有缺月,lag 可能会错位。这种情况下,还是 self join 更稳。
四、环比 SQL 怎么写
环比本质上就是和上一期比较。
with month_gmv as ( select date_trunc('month', order_date) as month_dt, sum(amount) as gmv from orders group by 1 ) select month_dt, gmv, lag(gmv, 1) over(order by month_dt) as prev_gmv, case when lag(gmv, 1) over(order by month_dt) is null or lag(gmv, 1) over(order by month_dt) = 0 then null else (gmv - lag(gmv, 1) over(order by month_dt)) / lag(gmv, 1) over(order by month_dt) end as mom_rate from month_gmv order by month_dt;
别把“环比”理解得太窄。它不只是月环比,而是当前周期和上一周期比。
五、累计值 SQL 怎么写
累计值最常见的写法就是窗口函数 sum over。
比如按天累计销售额:
with day_gmv as ( select order_date, sum(amount) as gmv from orders group by order_date ) select order_date, gmv, sum(gmv) over( order by order_date rows between unbounded preceding and current row ) as cumulative_gmv from day_gmv order by order_date;
如果要算年累计,就按年份分区。
sum(gmv) over( partition by extract(year from order_date) order by order_date rows between unbounded preceding and current row )