SQL 月度 YOY(保持月度数据频率的百分比变化)?

SQL for Monthly YOY (percentage change while keeping the monthly data frequency)?

假设我有一个 table 称为每月指标。下面 table 中的示例行:

EOM BRAND METRIC
1/31/2021 Nike 100
2/28/2021 Adidas 68
1/31/2022 Nike 110
2/28/2022 Adidas 68
... ... ...

我如何获得:

EOM BRAND METRIC YOY_CHANGE
1/31/2021 Nike 100 Null
1/31/2021 Adidas 68 Null
1/31/2022 Nike 110 10%
2/28/2022 Adidas 68 0
... ... ... ...

会喜欢在 Snowflake 中工作的东西(SQL:ANSI),但欢迎任何一般性想法。

CASE 语句和 LAG 函数可以做到这一点:

WITH data(EOM, BRAND, METRIC) AS (
    SELECT to_date(column1, 'mm/dd/yyyy'), column2, column3
    FROM VALUES
        ('1/31/2021','Nike',100),
        ('2/28/2021','Adidas',68),
        ('1/31/2022','Nike',110),
        ('2/28/2022','Adidas',68),
        ('2/08/2022','Tesla',0),
        ('2/08/2022','Tesla',99999)
)
SELECT 
    eom,
    brand,
    metric,
    lag(metric)over(partition by brand order by eom) as prior_metic,
    case 
        when prior_metic is null then null
        when prior_metic = metric then '0'
        when prior_metic = 0 then '+infinity'
        else round(((metric - prior_metic) / prior_metic)*100,0)::text || '%'
    end as YOY_CHANGE
FROM data
ORDER BY 1,2;
EOM BRAND METRIC PRIOR_METIC YOY_CHANGE
2021-01-31 Nike 100
2021-02-28 Adidas 68
2022-01-31 Nike 110 100 10%
2022-02-08 Tesla 0
2022-02-08 Tesla 99,999 0 +infinity
2022-02-28 Adidas 68 68 0

如果你想要严格的 ASNI 则不允许重用 prior_metric 所以你将 LAG 捣碎 N 次,希望 DB 足够聪明..

SELECT 
    eom,
    brand,
    metric,
    case 
        when lag(metric)over(partition by brand order by eom) is null then null
        when lag(metric)over(partition by brand order by eom) = metric then '0'
        when lag(metric)over(partition by brand order by eom) = 0 then '+infinity'
        else round(((metric - lag(metric)over(partition by brand order by eom)) / lag(metric)over(partition by brand order by eom))*100,0)::text || '%'
    end as YOY_CHANGE
FROM data
ORDER BY 1,2;

或使用 CTE/Sub-select:

SELECT 
    eom,
    brand,
    metric,
    case 
        when prior_metric is null then null
        when prior_metric = metric then '0'
        when prior_metric = 0 then '+infinity'
        else round(((metric - prior_metric) / prior_metric)*100,0)::text || '%'
    end as YOY_CHANGE
FROM (
    SELECT 
        eom,
        brand,
        metric,
        lag(metric)over(partition by brand order by eom) as prior_metric
    FROM data
)
ORDER BY 1,2;