优化 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))