CLPatterson
CLPatterson

Reputation: 113

Optimizing sum() over(order by...) clause throwing 'resources exceeded' error

I'm computing a sessions table from event data from out website in BigQuery. The events table has around 12 million events (pretty small). After I add in the logic to create sessions, I want to sum all sessions and assign a global_session_id. I'm doing that using a sum()over(order by...) clause which is throwing a resources exceeded error. I know that the order by clause is causing all the data to be processed on a single node and that is causing the compute resources to be exceeded, but I'm not sure what changes I can make to my code to achieve the same result. Any work arounds, advice, or explanations are greatly appreciated.

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

Upvotes: 0

Views: 184

Answers (1)

Gordon Linoff
Gordon Linoff

Reputation: 1271003

If might help if you defined a CTE with just the sessions, rather than at the event level. If this works:

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;

If that doesn't work, you can construct the global id:

You can join this back to the original event-level data and then use a max() window function to assign it to all events. Something like:

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;

If not, you can do:

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;

This might still fail if almost all users are unique and the sessions are short.

Upvotes: 1

Related Questions