Untuk menghitung persentase sesi yang membawa lead, bentuk satu baris per sesi terlebih dahulu. Sesudah itu, tentukan halaman pertama yang teramati dan apakah sesi tersebut mempunyai setidaknya satu event lead. Membagi jumlah event lead dengan seluruh baris ekspor GA4 akan menghasilkan rasio yang berbeda.
Saya akan memakai pertanyaan yang sempit: dari sesi yang diatribusikan ke google / organic, berapa yang memuat generate_lead, dikelompokkan menurut landing page? Hasilnya membantu meninjau halaman masuk. Ia belum menghitung calon pelanggan berkualitas atau membuktikan halaman menyebabkan penjualan.
Panduan ini ditujukan kepada analis yang sudah memahami SELECT dan GROUP BY, mempunyai ekspor GA4 di BigQuery, serta berwenang membaca datanya. Jika istilah sumber sesi masih membingungkan, baca dahulu scope akuisisi GA4.
Tetapkan satuan sebelum menulis query
Satu baris pada tabel event bukan satu sesi. Satu sesi dapat berisi beberapa page_view, klik, dan pengiriman formulir.
Dalam contoh ini, kunci sesi adalah kombinasi stream_id, user_pseudo_id, dan ga_session_id. ID sesi saja tidak cukup: dua pengguna dapat memulai sesi pada detik yang sama. Google menjelaskan bahwa ID sesi otomatis dibuat dari waktu mulai sesi dalam detik dan perlu dipasangkan dengan identitas pengguna saat dianalisis di luar GA4. Definisi sesi Google Analytics.
Definisi hitungan kita:
| Komponen | Keputusan |
|---|---|
| Penyebut | Sesi teramati dengan identitas valid dan sumber sesi tunggal google / organic |
| Pembilang | Sesi pada penyebut yang mempunyai minimal satu generate_lead |
| Landing page | URL dari page_view pertama yang teramati |
| Periode | Tanggal mulai sesi menurut zona waktu properti |
| Landing tidak diketahui | Tetap kelompok tersendiri; tidak diganti menjadi homepage |
| Sumber konflik atau urutan landing ambigu | Dikeluarkan dari rasio dan dihitung dalam tabel pemeriksaan |
Pastikan generate_lead memang dikirim setelah permintaan diterima sistem. Jika definisinya hanya klik tombol, query yang benar tetap menghitung kejadian yang keliru.
Periksa ekspor dan jendela tanggal
Gunakan tabel harian events_YYYYMMDD yang telah tersedia. Jangan menjumlahkan tabel harian dengan events_intraday_YYYYMMDD untuk tanggal yang sama. Ekspor streaming bersifat best effort dan dapat mempunyai gap; data ekspor juga tidak memuat seluruh tambahan pengolahan yang ada pada laporan GA4. Pilihan dan batas BigQuery Export.
Tanggal sesi dan tanggal tabel menjawab hal berbeda. Misalnya, sesi dimulai 7 Oktober pukul 23.58 WIB dan event lead terjadi 8 Oktober pukul 00.04 WIB. Untuk cohort sesi 1–7 Oktober, event pada 8 Oktober itu tetap dibutuhkan.
Query di bawah memindai 30 September–9 Oktober sebagai contoh, lalu memilih sesi yang dimulai 1–7 Oktober. Rentang tambahan tersebut bukan jaminan semua sesi lengkap. Periksa sesi yang menyentuh batas pemindaian dan perluas rentang bila diperlukan. Sesi yang panjang atau ekspor yang baru dimulai dapat tetap terpotong.
Catat kapan query dijalankan. Tabel harian dapat diperbarui karena event terlambat. Untuk laporan rutin, tetapkan kapan periode dianggap cukup matang dan kapan perhitungan diulang. Jangan menyamakan “tabel sudah ada” dengan “semua kejadian sudah masuk”.
Bentuk tabel sesi lalu hitung rasionya
Ganti nama project, dataset, dan stream_id dengan milikmu. Sesuaikan zona waktu serta nama event. Query memakai field sumber sesi session_traffic_source_last_click.cross_channel_campaign, bukan traffic_source yang menyimpan asal akuisisi awal pengguna.
Periksa bahwa field tersebut tersedia pada tabel dalam rentangmu. ga_session_id dan page_location diambil dari parameter berulang event_params. Jika schema berbeda, perbaiki pemetaan secara eksplisit; jangan mengganti sumber sesi dengan sumber pengguna agar query sekadar berjalan. Schema ekspor GA4.
-- GoogleSQL. Jalankan sebagai satu script setelah mengganti placeholder.
DECLARE period_start DATE DEFAULT DATE '2026-10-01';
DECLARE period_end DATE DEFAULT DATE '2026-10-07';
DECLARE property_tz STRING DEFAULT 'Asia/Jakarta';
DECLARE lead_name STRING DEFAULT 'generate_lead';
CREATE TEMP TABLE event_rows AS
SELECT
stream_id,
NULLIF(user_pseudo_id, '') AS visitor_id,
event_name,
event_timestamp,
batch_page_id,
batch_ordering_id,
batch_event_index,
(SELECT value.int_value FROM UNNEST(event_params)
WHERE key = 'ga_session_id') AS sid,
(SELECT value.string_value FROM UNNEST(event_params)
WHERE key = 'page_location') AS page_url,
NULLIF(session_traffic_source_last_click
.cross_channel_campaign.source, '') AS src,
NULLIF(session_traffic_source_last_click
.cross_channel_campaign.medium, '') AS med
FROM `YOUR_PROJECT.analytics_YOUR_PROPERTY.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260930' AND '20261009'
AND REGEXP_CONTAINS(_TABLE_SUFFIX, r'^\d{8}$')
AND platform = 'WEB'
AND stream_id = 'YOUR_WEB_STREAM_ID';
CREATE TEMP TABLE session_rows AS
WITH valid_events AS (
SELECT *
FROM event_rows
WHERE visitor_id IS NOT NULL
AND sid > 0
AND SAFE.TIMESTAMP_SECONDS(sid) IS NOT NULL
), ranked_pages AS (
SELECT *,
DENSE_RANK() OVER (
PARTITION BY stream_id, visitor_id, sid
ORDER BY event_timestamp, batch_page_id,
batch_ordering_id, batch_event_index
) AS page_order
FROM valid_events
WHERE event_name = 'page_view'
), landings AS (
SELECT stream_id, visitor_id, sid,
ANY_VALUE(page_url) AS observed_landing,
COUNT(DISTINCT TO_JSON_STRING(STRUCT(page_url))) > 1
AS landing_tie
FROM ranked_pages
WHERE page_order = 1
GROUP BY stream_id, visitor_id, sid
), grouped AS (
SELECT
stream_id, visitor_id, sid,
DATE(SAFE.TIMESTAMP_SECONDS(sid), property_tz) AS session_date,
ARRAY_AGG(
IF(src IS NOT NULL AND med IS NOT NULL,
STRUCT(src AS source, med AS medium), NULL)
IGNORE NULLS ORDER BY event_timestamp LIMIT 1
)[SAFE_OFFSET(0)] AS acquisition,
COUNT(DISTINCT IF(src IS NOT NULL AND med IS NOT NULL,
TO_JSON_STRING(STRUCT(src, med)), NULL)) AS source_versions,
COUNTIF(event_name = lead_name) > 0 AS has_lead
FROM valid_events
GROUP BY stream_id, visitor_id, sid
)
SELECT grouped.*, landings.observed_landing,
CASE
WHEN session_date NOT BETWEEN period_start AND period_end
THEN 'outside_period'
WHEN source_versions = 0 THEN 'source_unknown'
WHEN source_versions > 1 THEN 'source_conflict'
WHEN acquisition.source != 'google'
OR acquisition.medium != 'organic' THEN 'other_source'
WHEN landings.landing_tie THEN 'landing_tie'
ELSE 'included'
END AS disposition
FROM grouped
LEFT JOIN landings USING (stream_id, visitor_id, sid);
-- Hasil 1: baris event yang tidak bisa membentuk identitas sesi.
SELECT COUNT(*) AS scanned_event_rows,
COUNTIF(visitor_id IS NULL OR sid IS NULL OR sid <= 0
OR SAFE.TIMESTAMP_SECONDS(sid) IS NULL) AS invalid_identity_rows
FROM event_rows;
-- Hasil 2: jangan menyembunyikan sesi yang dikecualikan.
SELECT disposition, COUNT(*) AS observed_sessions
FROM session_rows
GROUP BY disposition;
-- Hasil 3: rasio pada cohort yang memenuhi kontrak.
SELECT observed_landing,
COUNT(*) AS observed_sessions,
COUNTIF(has_lead) AS sessions_with_lead,
SAFE_DIVIDE(COUNTIF(has_lead), COUNT(*)) AS session_lead_rate
FROM session_rows
WHERE disposition = 'included'
GROUP BY observed_landing
ORDER BY observed_sessions DESC;
Tiga hasil tersebut harus dibaca bersama. Angka pada hasil terakhir tidak mewakili sesi yang masuk source_unknown atau source_conflict. Kategori pemeriksaan menggunakan urutan pada CASE, sehingga setiap sesi memperoleh satu alasan utama.
Sebelum query berbayar, periksa estimasi pemindaian melalui dry run dan batas biaya yang berlaku pada project. Pada model on-demand, pengaturan maximum bytes billed dapat membatasi byte yang ditagihkan. Menambahkan LIMIT 10 pada hasil bukan cara umum membatasi pemindaian tabel. Kontrol biaya BigQuery.
Uji dengan data kecil yang hasilnya diketahui
Gunakan contoh ilustratif berikut sebagai fixture, yaitu data uji dengan jawaban acuan. Semua sesi berada dalam periode utama kecuali waktu event lanjutan yang disebutkan.
| Sesi | Kejadian penting | Hasil yang diharapkan |
|---|---|---|
| A | Landing /panduan/, dua lead event |
Satu sesi, satu sesi dengan lead |
| B | Pengguna berbeda dari A, ID sesi sama, tanpa lead | Tetap sesi berbeda |
| C | Landing /panduan/ pada 7 Oktober 23.58; lead pada 8 Oktober 00.04 |
Satu sesi dengan lead dalam cohort 1–7 Oktober |
| D | Ada lead, tidak ada page_view |
Satu sesi dengan landing tidak diketahui |
| E | Sumber sesi email | other_source |
| F | Dua pasangan sumber lengkap berbeda pada satu sesi | source_conflict |
| G | Tidak ada pasangan source/medium lengkap | source_unknown |
| H | Dua URL pertama berbeda dengan semua penanda urutan sama | landing_tie |
| Baris tambahan | Identitas pengguna kosong | Masuk jumlah baris identitas tidak valid |
Untuk A, B, dan C, tetapkan sumber tunggal Google organic serta landing yang sama. D juga Google organic. Hasil /panduan/ harus 3 sesi, 2 sesi dengan lead, rasio 2/3 atau sekitar 66,67%. Kelompok landing tidak diketahui berisi 1 sesi dan 1 sesi dengan lead.
Mengubah A dari satu menjadi dua lead event tidak boleh mengubah rasio. Memotong scan pada 7 Oktober dapat menghilangkan lead C; ini contoh mengapa tanggal scan tidak boleh disamakan begitu saja dengan tanggal cohort.
Perbaiki kegagalan sebelum menafsirkan halaman
Jika scalar subquery menghasilkan lebih dari satu nilai, periksa apakah parameter dengan key yang sama muncul ganda dalam satu event. Jangan menambahkan LIMIT 1 tanpa memahami duplikasinya.
Jika semua sumber kosong, periksa schema, periode, jenis ekspor, dan implementasi pengumpulan. Jika identitas kosong, laporkan jumlah barisnya; jangan menggabungkan semuanya sebagai satu pengguna anonim. Kondisi consent dan pengumpulan dapat membatasi data, tetapi query ini tidak menentukan penyebab setiap nilai kosong.
Jika beberapa page_view mempunyai timestamp sama, penanda batch membantu pengurutan. Query mengumpulkan seluruh kandidat dengan urutan pertama yang sama melalui DENSE_RANK. Bila URL kandidat berbeda, sesi masuk landing_tie; pilihan ANY_VALUE pada kelompok tersebut tidak dipakai untuk menghitung rasio. Jika semua kandidat menunjuk URL yang sama, landing masih dapat ditentukan. Fungsi pengurutan BigQuery.
Terakhir, periksa URL sebelum hasil dibagikan. Parameter bisa memuat informasi yang tidak pantas masuk dashboard. Buat kunci halaman yang aman sesuai struktur situs, sambil mempertahankan parameter yang memang membedakan isi.
Simpan SQL, periode scan, waktu pengambilan, tiga tabel hasil, dan pemeriksaan fixture. Pekerjaan selesai ketika satuan serta pengecualiannya dapat dijelaskan, bukan ketika rasio terlihat tinggi.




