优化sum()over(order by ...)子句会引发“超出资源”错误

时间:2019-03-25 18:58:05

标签: sql google-bigquery window-functions

我正在使用BigQuery中来自网站的事件数据计算会话表。事件表中约有1200万个事件(相当小)。在添加了创建会话的逻辑之后,我想对所有会话求和并分配一个global_session_id。我正在使用sum()over(order by...)子句执行此操作,该子句抛出资源超出错误。我知道order by子句导致所有数据在单个节点上进行处理,并导致超出计算资源,但是我不确定我可以对代码进行哪些更改以达到相同的结果。任何解决方法,建议或解释将不胜感激。

with sessions_1 as ( /* Tie a visitor's last event and last campaign to current event. */

                       select visitor_id as session_user_id,
                              sent_at,
                              context_campaign_name,
                              event,
                              id,
                              LAG(sent_at,1) OVER (PARTITION BY visitor_id ORDER BY sent_at) as last_event,
                              LAG(context_campaign_name,1) OVER (PARTITION BY visitor_id ORDER BY sent_at) as last_event_campaign_name 
                         from tracks_2
                    ),

sessions_2 as ( /* Flag events that begin a new session. */

                   select *,
                   case 
                     when context_campaign_name != last_event_campaign_name
                       or context_campaign_name is null and last_event_campaign_name is not null
                       or context_campaign_name is not null and last_event_campaign_name is null
                       then 1
                     when unix_seconds(sent_at) 
                          - unix_seconds(last_event) >= (60 * 30)
                       or last_event is null
                       then 1 
                       else 0 
                       end as is_new_session
                   from sessions_1
                    ),

sessions_3 as ( /* Assign events sessions numbers for total sessions and total user sessions. */

                     select id as event_id,
                            sum(is_new_session) over (order by session_user_id, sent_at) as global_session_id
                            #sum(is_new_session) over (partition by session_user_id order by sent_at) as user_session_id
                      from  materialized_result_of_sessions_2_query 
                      )
select * from sessions_3

1 个答案:

答案 0 :(得分:1)

如果仅通过会话而不是在事件级别定义CTE,可能会有所帮助。如果可行:

select session_user_id, sent_at,
       row_number() over (order by session_user_id, sent_at) as global_session_id
from  materialized_result_of_sessions_2_query 
where is_new_session
group by session_user_id, sent_at;

如果这不起作用,则可以构造全局ID:

您可以将其重新连接到原始事件级数据,然后使用max()窗口函数将其分配给所有事件。像这样:

select e.*,
       max(s.global_session_id) over (partition by e.session_user_id order by e.event_at) as global_session_id
from events e left join
     (<above query>) s
     on s.session_user_id = e.session_user_id and s.sent_at = e.event_at;

如果没有,您可以这样做:

select us.*, us.user_session_id + s.offset as global_session_id
from (select session_user_id, sent_at,
             row_number() over (partition by session_user_id order by sent_at) as user_session_id
      from materialized_result_of_sessions_2_query 
      where is_new_session
     ) us join
     (select session_user_id, count(*) as cnt,
             sum(count(*)) over (order by session_user_id) - count(*) as offset
      from materialized_result_of_sessions_2_query
      where is_new_session
      group by session_user_id
     ) s
     on us.session_user_id = s.session_user_id;

如果几乎​​所有用户都是唯一的并且会话很短,这可能仍然会失败。