(i dlaczego logika jest ważniejsza niż sam SQL)
Jeśli pracujesz z danymi GA4 w BigQuery, prędzej czy później trafisz na ten problem:
jak poprawnie odtworzyć „First User Primary Channel Group” poza interfejsem GA4.
Niby proste pole.
A w praktyce — złożona logika, dziesiątki warunków i łatwy sposób, żeby się pomylić.
Ten wpis pokazuje jak podejść do tego zadania poprawnie i dlaczego kluczem nie jest kopiowanie SQL-a, tylko zrozumienie logiki stojącej za channel groupingiem.
Dlaczego „First User Primary Channel Group” jest problematyczne?
W interfejsie Google Analytics 4:
- wszystko „po prostu działa”
- channel grouping jest liczony automatycznie
W BigQuery:
- dostajesz surowe dane
- całą logikę musisz odtworzyć sam
I tu zaczynają się schody.
Fundament: logika GA4 Default Channel Group
Podstawą jest GA4 Default Channel Group, czyli zestaw reguł opartych na:
traffic_source.sourcetraffic_source.mediumtraffic_source.name(kampania)
Każdy kanał (Direct, Paid Search, Organic Social itd.) to:
- konkretne warunki
- regexy
- listy źródeł
- kombinacje source + medium + campaign
Bez tej logiki:
➡️ wyniki nigdy nie będą zgodne z GA4.
Krok 1: zdefiniuj kanały (bez SQL-a)
Zanim napiszesz choć jedną linijkę zapytania, musisz mieć jasną definicję kanałów.
Przykładowo:
- Direct
- source =
(direct) - medium =
(not set)lub(none)
- source =
- Paid Shopping
- source = platformy zakupowe LUB
- campaign zawiera „shop / shopping”
- medium =
cpc / ppc / paid
- Paid Search / Paid Social / Paid Video
- osobne listy źródeł
- medium płatne
To jest najbardziej czasochłonna część pracy — i jednocześnie najważniejsza.
Krok 2: tylko użytkownicy z session_start
Żeby policzyć First User:
- bierzesz tylko użytkowników, którzy wywołali event
session_start - pracujesz na
user_pseudo_id
To pozwala:
- uniknąć zduplikowanych użytkowników
- zachować spójność z raportami GA4
Krok 3: agregacja po użytkowniku
Dalej:
- przypisujesz jeden channel group do użytkownika
- liczysz distinct user_pseudo_id
Efekt:
- realna liczba użytkowników pozyskanych z danego kanału
- a nie liczba eventów czy sesji
Krok 4: procentowy udział kanałów
Żeby raport był użyteczny:
- liczysz udział % każdego kanału
- odnosisz go do całkowitej liczby użytkowników
To dokładnie to, co widzisz w raportach akwizycji GA4 — tylko w wersji SQL.
Krok 5: wiersz „Total”
Mała rzecz, a robi ogromną różnicę:
- suma użytkowników
- 100% udziału
- łatwe sanity-checki wyników
Jeśli suma się nie zgadza → logika jest błędna.
SQL to efekt końcowy, nie punkt startu
Finalnie:
- zapytanie ma ponad 100 linii SQL
- ale jego wygenerowanie zajmuje sekundy
-- Calculating First User Primary Channel Group.
WITH
extracted_users AS (
SELECT
user_pseudo_id,
CASE
WHEN
traffic_source.source = '(direct)'
AND traffic_source.medium IN ('(not set)', '(none)')
THEN 'Direct'
WHEN
(
regexp_contains(
traffic_source.source,
'alibaba|amazon|google shopping|shopify|etsy|ebay|stripe|walmart')
OR regexp_contains(
traffic_source.name, '^(.*(([^a-df-z]|^)shop|shopping).*)$'))
AND regexp_contains(traffic_source.medium, '^(.*cp.*|ppc|paid.*)$')
THEN 'Paid Shopping'
WHEN
regexp_contains(
traffic_source.source,
'baidu|bing|duckduckgo|ecosia|google|yahoo|yandex')
AND regexp_contains(traffic_source.medium, '^(.*cp.*|ppc|paid.*)$')
THEN 'Paid Search'
WHEN
regexp_contains(
traffic_source.source,
'badoo|facebook|fb|instagram|linkedin|pinterest|tiktok|twitter|whatsapp')
AND regexp_contains(traffic_source.medium, '^(.*cp.*|ppc|paid.*)$')
THEN 'Paid Social'
WHEN
regexp_contains(
traffic_source.source,
'dailymotion|disneyplus|netflix|youtube|vimeo|twitch')
AND regexp_contains(traffic_source.medium, '^(.*cp.*|ppc|paid.*)$')
THEN 'Paid Video'
WHEN
traffic_source.medium IN (
'display', 'banner', 'expandable', 'interstitial', 'cpm')
THEN 'Display'
WHEN
regexp_contains(
traffic_source.source,
'alibaba|amazon|google shopping|shopify|etsy|ebay|stripe|walmart')
OR regexp_contains(
traffic_source.name, '^(.*(([^a-df-z]|^)shop|shopping).*)$')
THEN 'Organic Shopping'
WHEN
regexp_contains(
traffic_source.source,
'badoo|facebook|fb|instagram|linkedin|pinterest|tiktok|twitter|whatsapp')
OR traffic_source.medium IN (
'social', 'social-network', 'social-media', 'sm', 'social network',
'social media')
THEN 'Organic Social'
WHEN
regexp_contains(
traffic_source.source,
'dailymotion|disneyplus|netflix|youtube|vimeo|twitch')
OR regexp_contains(traffic_source.medium, '^(.*video.*)$')
THEN 'Organic Video'
WHEN
regexp_contains(
traffic_source.source,
'baidu|bing|duckduckgo|ecosia|google|yahoo|yandex')
OR traffic_source.medium = 'organic'
THEN 'Organic Search'
WHEN
regexp_contains(traffic_source.source, 'email|e-mail|e_mail|e mail')
OR regexp_contains(
traffic_source.medium, 'email|e-mail|e_mail|e mail')
THEN 'Email'
WHEN traffic_source.medium = 'affiliate' THEN 'Affiliates'
WHEN traffic_source.medium = 'referral' THEN 'Referral'
WHEN traffic_source.medium = 'audio' THEN 'Audio'
WHEN traffic_source.medium = 'sms' THEN 'SMS'
WHEN
traffic_source.medium LIKE '%push'
OR regexp_contains(traffic_source.medium, 'mobile|notification')
THEN 'Mobile Push Notifications'
ELSE 'Unassigned'
END AS channel_grouping
FROM
`dbrt-ga4.analytics_207472454.events_*`
WHERE
_table_suffix BETWEEN '20241001' AND '20241031'
AND user_pseudo_id IS NOT NULL
AND event_name = 'session_start'
AND traffic_source.source IS NOT NULL
AND traffic_source.medium IS NOT NULL
),
aggregated AS (
SELECT
channel_grouping AS first_user_primary_channel_group,
COUNT(DISTINCT user_pseudo_id) AS total_users
FROM extracted_users
GROUP BY channel_grouping
),
final AS (
SELECT
first_user_primary_channel_group,
total_users,
round(100 * total_users / sum(total_users) OVER (), 2) AS percent_of_total
FROM aggregated
UNION ALL
SELECT
'Total',
sum(total_users),
100.00
FROM aggregated
)
SELECT *
FROM final
ORDER BY
CASE WHEN first_user_primary_channel_group = 'Total' THEN 0 ELSE 1 END,
total_users DESC

Cały czas schodzi na:
👉 zrozumienie logiki channel groupingu
Bez tego:
- nawet poprawny syntaktycznie SQL
- da błędne dane
Dlaczego to podejście się skaluje?
Bo gdy rozumiesz logikę, możesz:
- użyć tego samego channel groupingu z innymi metrykami
- testować alternatywne definicje kanałów
- budować raporty użytkowników, sesji, przychodu
- automatyzować analizy zamiast pisać SQL od zera
Gotowy SQL to jedno zastosowanie.
Zrozumiana logika → N zastosowań.
Podsumowanie
Liczenie First User Primary Channel Group w GA4 BigQuery to nie jest problem SQL-owy.
To problem koncepcyjny.
Jeśli:
- rozumiesz GA4 Default Channel Group
- wiesz, jak działa source / medium / campaign
- nie mieszasz scope’ów
to:
SQL staje się tylko narzędziem, a nie przeszkodą.