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;
假设我有一个 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;