繁体   English   中英

填充雪花中缺失的数据

[英]Filling in missing data in Snowflake

我在雪花中有一张这样的桌子:

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 中执行此操作的查询?

您可以为此使用conditional_change_event函数 - 此处记录

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       |
+----+-----+--------+--------+

您可以使用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()first_value()

select t.*,
       last_value(nullif(item, 0) ignore nulls) over (partition by user order by time desc)
from t;

如果您想在 Snowflake 中使用 first_value() 或 last_value(),请记住,Snowflake 支持的窗口框架与此处记录的 ANSI 标准不同。 这意味着,如果您想使用默认窗口框架 RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW,您必须在语句中明确包含它,否则,默认值为 ROWS BETWEEN UNBOUNDED PRECEDING AND 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;

上述解决方案没有错......但这里有一种不同的方法......我认为它更简单。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM