Условие
На базе bigquery-public-data.ga4_obfuscated_sample_ecommerce выполнить за 2020 год:
- Воронка закрытого типа по пользователям с шагами
select_item → view_item → add_to_cart → purchase. Возможность фильтрации по названию товара. - Многоканальная последовательность от первой сессии до
purchase— цепочкиsource/mediumи количество пользователей по каждой цепочке. - Time-lag: разница в днях между первым визитом и первой покупкой.
- Когортный анализ: weekly retention, когорта = неделя первого визита, идентификатор —
user_pseudo_id.
Решение
1. Закрытая воронка
WITH evt AS (
SELECT
user_pseudo_id,
event_name,
TIMESTAMP_MICROS(event_timestamp) AS ts,
(SELECT value.string_value FROM UNNEST(items) WHERE key = 'item_name') AS item_name
FROM `bigquery-public-data.ga4_obfuscated_sample_ecommerce.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20200101' AND '20201231'
AND event_name IN ('select_item','view_item','add_to_cart','purchase')
),
firsts AS (
SELECT user_pseudo_id, event_name, MIN(ts) AS first_ts
FROM evt
GROUP BY 1, 2
)
SELECT
COUNT(DISTINCT IF(event_name='select_item', user_pseudo_id, NULL)) AS s1,
COUNT(DISTINCT IF(event_name='view_item' AND ts > sel.first_ts, user_pseudo_id, NULL)) AS s2,
-- Закрытость → каждое следующее ts > предыдущего
...
FROM firsts;В реальности удобнее на каждый шаг join с предыдущим по user_pseudo_id и event_ts >= prev_ts.
2. Мульти-канальная последовательность
WITH first_sess AS (
SELECT user_pseudo_id,
ARRAY_AGG(STRUCT(traffic_source.source AS src, traffic_source.medium AS med)
ORDER BY event_timestamp LIMIT 1)[OFFSET(0)] AS first_src
FROM events
WHERE event_name = 'session_start'
GROUP BY user_pseudo_id
),
chain AS (
SELECT user_pseudo_id,
STRING_AGG(DISTINCT traffic_source.source || '/' || traffic_source.medium,
' → ' ORDER BY event_timestamp) AS path
FROM events
WHERE event_name = 'session_start'
AND event_timestamp <= (SELECT MIN(event_timestamp) FROM events e2
WHERE e2.user_pseudo_id = events.user_pseudo_id
AND e2.event_name = 'purchase')
GROUP BY user_pseudo_id
)
SELECT path, COUNT(*) AS users
FROM chain
GROUP BY path
ORDER BY users DESC;3. Time-lag
SELECT
user_pseudo_id,
DATE_DIFF(DATE(MIN(IF(event_name='purchase', ts, NULL))),
DATE(MIN(ts)),
DAY) AS days_to_first_purchase
FROM evt
GROUP BY user_pseudo_id
HAVING days_to_first_purchase IS NOT NULL;
-- Среднее
SELECT AVG(days_to_first_purchase) FROM ...;4. Weekly retention
WITH first_visit AS (
SELECT user_pseudo_id,
DATE_TRUNC(DATE(TIMESTAMP_MICROS(MIN(event_timestamp))), WEEK) AS cohort
FROM events GROUP BY 1
),
visits AS (
SELECT user_pseudo_id,
DATE_TRUNC(DATE(TIMESTAMP_MICROS(event_timestamp)), WEEK) AS visit_week
FROM events
)
SELECT
f.cohort,
DATE_DIFF(v.visit_week, f.cohort, WEEK) AS week_n,
COUNT(DISTINCT v.user_pseudo_id) AS users
FROM first_visit f
JOIN visits v USING(user_pseudo_id)
GROUP BY 1, 2
ORDER BY 1, 2;Подводные камни
- GA4 хранит события с дублированием — фильтровать по
event_bundle_sequence_idилиIS_TRUE(...)для уникальности. _TABLE_SUFFIXдля performance — обязательно с_TABLE_DATE_RANGEили предикатом по диапазону.- Часовой пояс:
event_timestampв UTC; product может ожидать локальный TZ. - У GA4 атрибуция «первое касание» отличается от «последнее не direct» — уточнить у заказчика.
Эталонный ответ
4 запроса BigQuery: воронка через MIN(ts) на каждом шаге с условием возрастания; цепочка source/medium до первой покупки; DATE_DIFF для time-lag; когорты по неделе первого визита.