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

Posted

技术标签:

【中文标题】优化 sum() over(order by...) 子句抛出“超出资源”错误【英文标题】:Optimizing sum() over(order by...) clause throwing 'resources exceeded' error 【发布时间】:2019-03-25 18:58:05 【问题描述】:

我正在 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

【问题讨论】:

样本数据、期望的结果以及对会话定义的描述会很有帮助。 会话定义是 30 分钟不活动窗口或当用户通过不同的广告访问网站时。 期望的结果是一个包含所有唯一事件 api id、每个会话的唯一 id 以及给定用户的第 n 个会话的字段的表。处理示例数据。 为什么唯一的 id 必须是一个连续的值?或者这不是一个要求? @GordonLinoff 如果它不是一个顺序值,它将影响我们在下游所做的一些配置,所以理想情况下我想保留顺序值。我知道一种可能的解决方案可能是通过组合 user_id 和会话开始日期来创建 global_session_id,但想先看看是否可以使用顺序值。 【参考方案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;

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

【讨论】:

如果我不需要在事件级别分配的 global_session_id,那就可以了。在创建派生会话表的过程中,我创建了额外的布尔字段来描述给定会话中发生的事情(例如:购买的 = true、product_added = true 等)。如果没有 global_session_id,我无法轻松创建这些字段。 @CLStingle 。 . .我详细说明了您如何将这些信息传播到所有事件中。 此解决方案在加入回事件表时不起作用 b/c,它只会加入开始会话的事件,而不是存在于 is_new_session = 0 的会话中的事件。 @CLStingle 。 . .好点子。它必须是 left join,并对窗口功能进行一些调整。我调整了答案。

以上是关于优化 sum() over(order by...) 子句抛出“超出资源”错误的主要内容,如果未能解决你的问题,请参考以下文章

Oracle - SELECT DENSE_RANK OVER(ORDER BY、SUM、OVER 和 PARTITION BY)

SUM() OVER (PARTITION BY) AS - 存在重复项时

视图中的 SUM(...) OVER (ORDER BY ...) 导致性能不佳

MariaDB 查询 SUM() OVER (PARTITION BY)

Hive 问题 - Rank() OVER (PARTITION BY Dept ORDER BY sum(salary))

DB2——sum over partition by 的用法