【问题标题】:How to fill in rows based on available data如何根据可用数据填写行
【发布时间】:2020-09-04 21:41:27
【问题描述】:

使用雪花 SQL。

所以我的表有 2 列:小时和客户 ID。每个顾客将有 2 行,一个对应于他/她进入商店的时间,一个对应于他/她离开商店的时间。使用这些数据,我想创建一个表,其中包含客户在商店中的每一个小时。例如,客户 X 在下午 1 点进入商店并在下午 5 点离开,因此将有 5 行(每小时 1 行),如下面的屏幕截图。

这是我现在的尝试:

select
    hour
    ,first_value(customer_id) over (partition by customer_id order by hour rows between unbounded preceding and current row) as customer_id
FROM table

【问题讨论】:

  • 客户离开和返回时会发生什么。您没有声明要处理多个日常交易。另外,您为什么不将向 postgres 提出这个确切问题的任何教训应用于这个问题。对于您的第一个值,窗口范围值不会增加任何值,因为第一个值和 order 永远不会选择“以后的值”。最后调用列小时,当它的值中有一个日期不太正确,并且您的数据被限制为一天,因此该日期不可用,或者该日期有用,因此也打算由
  • stackoverflow.com/questions/61879511/… 鉴于您已经“无所事事”来推进您对这个问题的思考,更好的选择是“如何在雪花中执行cross join lateral generate_series,因为这似乎不起作用”类型的问题.

标签: sql date select group-by snowflake-cloud-data-platform


【解决方案1】:

所以对于测试数据中显示的示例案例,只有一天的数据,GMB 的解决方案可以正常工作。

一旦你进入很多天(可以/不能有重叠的商店访问,让我们假装你不能在商店过夜)

可以通过以下方式修复:

select t.hour::date, t.customer_id, min(t.hour) min_hour, max(t.hour) max_hour 
from mytable t
group by 1,2

但是多个条目,ether 需要标签数据,例如:

with mytable as (
  select * from values 
    ('2019-04-01 09:00:00','x','in')
    ,('2019-04-01 15:00:00','x','out')
    ,('2019-04-02 12:00:00','x','in')
    ,('2019-04-02 14:00:00','x','out')
   v(hour, customer_id, state)
)

或者为了它被推断:

with mytable as (
  select * from values ('2019-04-01 09:00:00','x','in'),('2019-04-01 15:00:00','x','out')
     ,('2019-04-02 12:00:00','x','in'),('2019-04-02 14:00:00','x','out')
   v(hour, customer_id, state)
)
select hour::date as day
    ,hour
    ,customer_id
    ,state
    ,BITAND(row_number() over(partition by day, customer_id order by hour), 1) = 1 AS in_dir
from mytable
order by 3,1,2;

给予:

DAY           HOUR                   CUSTOMER_ID    STATE    IN_DIR
2019-04-01    2019-04-01 09:00:00    x              in       TRUE
2019-04-01    2019-04-01 15:00:00    x              out      FALSE
2019-04-02    2019-04-02 12:00:00    x              in       TRUE
2019-04-02    2019-04-02 14:00:00    x              out      FALSE

现在这可以与 LAG 和 QUALIFY 一起使用以获得可以处理多条目的真实范围:

select customer_id
    ,day
    ,hour
    ,lead(hour) over (partition by customer_id, day order by hour) as exit_time
from infer_direction
qualify in_dir = true

它的工作原理是为每一天/客户的所有行获取下一次,然后(通过资格)只保留行'in' rows。

然后我们可以加入一天中的时间:

select dateadd('hour', row_number() over(order by null) - 1, '00:00:00'::time) as hour
from table (generator(rowcount => 24))

因此一切都交织在一起

with mytable as (
  select hour::timestamp as hour, customer_id, state 
  from values 
     ('2019-04-01 09:00:00','x','in')
     ,('2019-04-01 12:00:00','x','out')
     ,('2019-04-02 13:00:00','x','in')
     ,('2019-04-02 14:00:00','x','out')
     ,('2019-04-02 9:00:00','x','in')
     ,('2019-04-02 10:00:00','x','out')
   v(hour, customer_id, state)
), infer_direction AS (
  select hour::date as day
      ,hour::time as hour
      ,customer_id
      ,state
      ,BITAND(row_number() over(partition by day, customer_id order by hour), 1) = 1 AS in_dir
  from mytable
), visit_ranges as (
  select customer_id
      ,day
      ,hour
      ,lead(hour) over (partition by customer_id, day order by hour) as exit_time
  from infer_direction
  qualify in_dir = true
), time_of_day AS (
    select dateadd('hour', row_number() over(order by null) - 1, '00:00:00'::time) as hour
    from table (generator(rowcount => 24))
)
select t.customer_id
    ,t.day
    ,h.hour
from visit_ranges as t
join time_of_day h on h.hour between t.hour and t.exit_time
order by 1,2,3;

我们得到:

CUSTOMER_ID    DAY           HOUR
x              2019-04-01    09:00:00
x              2019-04-01    10:00:00
x              2019-04-01    11:00:00
x              2019-04-01    12:00:00
x              2019-04-02    09:00:00
x              2019-04-02    10:00:00
x              2019-04-02    13:00:00
x              2019-04-02    14:00:00

【讨论】:

  • 这是一个聪明的实现。目前,GMB 的解决方案完美地满足了需求。但肯定会重新审视您的建议。
【解决方案2】:

在 Snowflake 中,您通常会使用一个数字表来解决这个问题。您可以使用table (generator ...) 语法来生成这样的派生表,然后将其与聚合查询连接起来,该聚合查询计算每个客户端的小时边界并具有不等式条件:

select t.customer_id, dateadd(hour, n.rn, t.min_hour) final_hour
from (
    select t.customer_id, min(t.hour) min_hour, max(t.hour) max_hour 
    from mytable t
    group by t.customer_id
) t
inner join (
    select row_number() over(order by null) - 1 rn 
    from table (generator(rowcount => 24))
) n on dateadd(hour, n.rn, t.min_hour) <= t.max_hour
order by customer_id, final_hour

这将处理每位客户最多 24 小时的访问。如果你需要更多,那么你可以增加表格生成器的参数。

【讨论】:

  • 为受骗的帖子道歉,尝试自己进行雪花实施但没有成功。
  • row_number 应该是row_number()
猜你喜欢
  • 2020-09-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 2020-09-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多