填补雪花中缺失的数据
Posted
技术标签:
【中文标题】填补雪花中缺失的数据【英文标题】:Filling in missing data in Snowflake 【发布时间】:2021-07-23 21:37:04 【问题描述】:我在雪花中有一张这样的桌子:
TIME USER ITEM
1 frank 1
2 frank 0
3 frank 0
4 frank 0
5 frank 2
6 alf 5
7 alf 0
8 alf 6
9 alf 0
10 alf 9
我希望能够用下一个非零值替换所有零,所以最后我有一个这样的表:
TIME USER ITEM
1 frank 1
2 frank 2
3 frank 2
4 frank 2
5 frank 2
6 alf 5
7 alf 6
8 alf 6
9 alf 9
10 alf 9
如何编写在 Snowflake 中执行此操作的查询?
【问题讨论】:
这张表中数据的顺序是什么?您应该注意 SQL 中没有“自然顺序”,因此每次您执行其中的SELECT *
时,它们都可以以任意随机顺序出现(包括 0)
抱歉,我只是没有列出时间戳。我将编辑帖子。
项目在您的订购中是否像您的示例中那样严格增加(不包括零)?如果是这样,一个简单的运行 max() 将起作用
【参考方案1】:
您可以为此使用 conditional_change_event
函数 - documented here:
with base_table as (
select
t1.*,
conditional_change_event(item) over (order by time desc) event_num
from test_table t1
order by time desc
)
select
t1.time,
t1.user,
t1.item old_item,
coalesce(t2.item, t1.item) new_item
from base_table t1
left join base_table t2 on t1.event_num = t2.event_num + 1 and t1.item = 0
order by t1.time asc
以上 SQL 结果:
+----+-----+--------+--------+
|TIME|USER |OLD_ITEM|NEW_ITEM|
+----+-----+--------+--------+
|1 |frank|1 |1 |
|2 |frank|0 |2 |
|3 |frank|0 |2 |
|4 |frank|0 |2 |
|5 |alf |2 |2 |
|6 |alf |5 |5 |
|7 |alf |0 |6 |
|8 |alf |6 |6 |
|9 |alf |0 |9 |
|10 |alf |9 |9 |
+----+-----+--------+--------+
【讨论】:
【参考方案2】:你可以使用lead(ignore nulls)
:
select t.*,
(case when item = 0
then lead(nullif(item, 0) ignore nulls) over (partition by user order by time)
else item
end) as imputed_item
from t;
您也可以使用first_value()
:
select t.*,
last_value(nullif(item, 0) ignore nulls) over (partition by user order by time desc)
from t;
【讨论】:
+1 用于使用 LEAD IGNORE NULLS(我会使用 ZEROIFNULL())。 LAST_VALUE 在当前形式下不起作用,请检查(1,0,2,0,3)
-> (1,3,2,3,3)
而不是 (1,2,2,3,3)
@LukaszSzozda 。 . .我更喜欢NULLIF()
,因为它是标准 SQL。第二个应该与first_value()
一起使用。【参考方案3】:
如果您想在 Snowflake 中使用 first_value() 或 last_value(),请记住,Snowflake 支持的窗口框架不同于文档 here 中的 ANSI 标准。这意味着,如果您想使用默认窗口框架 RANGE BETWEEN UNBOUNDED PRECEDING 和 CURRENT ROW,您必须在语句中明确包含它,否则,默认值将是 ROWS BETWEEN UNBOUNDED PRECEDING 和 UNBOUNDED FOLLOWING,这就是为什么 LAST_VALUE 示例来自以前的答案将无法正常工作。这是一个可行的示例:
select t.*,
last_value(nullif(item, 0) ignore nulls) over (partition by user order by time desc rows between unbounded preceding and current row)
from t;
【讨论】:
【参考方案4】:上述解决方案没有错……但这里有一种不同的方法……我认为它更简单。
select * from good
union all
select
bad.time
,bad.user
,min(good.item)
from bad
left outer join
good on good.user=bad.user and good.time>bad.time
group by
1,2
完整复制|粘贴|运行 SQL:
with cte as (
select * from (
select 1 time, 'frank' user , 1 item union
select 2 time, 'frank' user , 0 item union
select 3 time, 'frank' user , 0 item union
select 4 time, 'frank' user , 0 item union
select 5 time, 'frank' user , 2 item union
select 6 time, 'alf' user , 5 item union
select 7 time, 'alf' user , 0 item union
select 8 time, 'alf' user , 6 item union
select 9 time, 'alf' user , 0 item union
select 10 time, 'alf' user , 9) )
, good as (select * from cte where item<> 0)
, bad as (select * from cte where item= 0)
select * from good
union all
select
bad.time
,bad.user
,min(good.item )
from bad
left outer join
good on good.user=bad.user and good.time>bad.time
group by
1,2
【讨论】:
以上是关于填补雪花中缺失的数据的主要内容,如果未能解决你的问题,请参考以下文章