Google Analytics 4

GA4 BigQuery SQL 查詢:基礎語法與巢狀欄位處理實戰

Open Data 4TW 編輯團隊 Open Data 4TW 編輯團隊
· · GA4, BigQuery, SQL 查詢

GA4 BigQuery 資料表結構與查詢前置準備

要對 GA4 資料執行 SQL 查詢,首要條件是將 GA4 資源的原始資料匯出至 BigQuery。這項操作需要在 GA4 管理介面中設定連結,之後系統會每日自動匯出原始事件資料。在設定連結時,可以選擇匯出範圍涵蓋「所有事件」與「使用者資料」兩大類別,其中「所有事件」會包含網站或應用程式上觸發的每一個事件記錄,而「使用者資料」則提供使用者層級的身份與屬性彙整資訊。兩者皆以每日為單位寫入獨立的資料表集合,因此查詢時必須明確指定日期範圍或使用萬用字元來涵蓋所需區間。根據 Google Cloud 的公開計價方式,每月有 1TB 的免費查詢額度,對多數中小型網站而言,基礎的分析查詢通常不會超過此限制。但若查詢範圍過大或未設定日期篩選條件,仍可能產生費用,因此建議養成每次查詢都限定日期範圍的習慣。此外,BigQuery 的計費方式是以「掃描的資料量」為基準,而非查詢結果的回傳大小,這代表即使最終結果只有幾百列,若未加上日期與其他欄位的過濾條件,掃描整張資料表仍可能消耗大量額度。

匯出後的資料表命名規則為 analytics_<property_id>.events_<YYYYMMDD>,其中 property_id 是 GA4 資源編號,日期則代表資料對應的匯出日期。每個日期會對應一張獨立的資料表,若要查詢多日資料,可使用萬用字元 events_* 搭配 _TABLE_SUFFIX 來簡化語法。舉例而言,events_* 會比對所有符合 events_ 前綴的資料表,而 _TABLE_SUFFIX 則是一個虛擬欄位,其值等於資料表名稱中萬用字元比對到的部分。透過 WHERE _TABLE_SUFFIX BETWEEN '20240101' AND '20240131' 即可精準鎖定特定日期區間,不必逐一撰寫各日期的完整表名,也可避免意外掃描到範圍外的資料表而增加查詢費用。

GA4 原始資料與一般關聯式資料庫的平面表格不同,包含大量巢狀(Nested)與重複(Repeated)欄位。例如 event_params 欄位內存放了所有事件的參數,以陣列形式存在,每列代表一筆參數記錄。若不瞭解這個資料模型,直接套用標準 SQL 語法通常會得到錯誤結果。巢狀欄位的概念類似於物件中的屬性,而重複欄位則類似於陣列。在 BigQuery 的資料表中,event_paramsRECORD 型別存在,並標記為 REPEATED,代表它是一個由多個結構化物件組成的陣列。每個物件內部的 keyvalue 子欄位分別保存參數名稱與參數值,而 value 本身也是一個巢狀結構,內部依資料型別拆分為多個欄位。這種設計能保留所有事件的完整參數資訊,但也使得標準 SQL 的語法無法直接對這些參數進行運算,必須先透過 UNNEST 將其展開。

理解巢狀與重複欄位

在 GA4 的 BigQuery 資料表中,event_params 是最典型的巢狀重複欄位範例。它內部包含 keyvalue 兩個子欄位,其中 value 又進一步拆分成 string_valueint_valuefloat_valuedouble_value。不同型別的參數會被存放在對應的值欄位中,未使用的型別欄位則為 NULL。查詢時必須使用 UNNEST() 函數將陣列展開,才能以一般欄位的方式進行篩選或提取。此處的型別判斷原則如下:若參數值為文字(如網址、事件名稱、來源媒介),則存入 string_value;若為整數(如工作階段編號、商品數量),則存入 int_value;若為帶有小數的數字,則可能存入 float_valuedouble_value,兩者的差異在於精確度與儲存大小。實際查詢時,如果對參數的型別不確定,可以先執行一個簡單的 SELECT 查看特定 event_params 的內容,確認其值欄位落在哪一欄。

以下為基本查詢結構的基礎範例,先以 UNNEST 展開事件參數,再取出特定條件:

SELECT
  event_name,
  ep.key AS param_key,
  ep.value.string_value AS param_value
FROM `project_id.analytics_xxxxx.events_20240101`,
UNNEST(event_params) AS ep
LIMIT 10

理解這個結構後,後續的指標計算才能建立在正確的基礎上。除了 event_params 之外,GA4 資料表中還有其他常見的巢狀重複欄位,例如 items(商品陣列)、user_properties(使用者屬性)以及 event_metadata 等。這些欄位的結構類似,皆以陣列形式保存多筆結構化資料,因此在查詢時都需要以 UNNESTGA4 BigQuery 資料表結構解析:欄位命名與三層架構,該文有更完整的欄位對照說明。

基礎 SQL 語法:計算使用者與工作階段指標

GA4 中「不重複使用者數」與「工作階段數」是兩項最基本也最常被引用的指標。在 BigQuery 中,這兩個指標的計算方式與 GA4 標準報表略有差異,必須理解所使用的欄位定義。不重複使用者可透過 COUNT(DISTINCT user_pseudo_id) 計算,而工作階段數則需組合 user_pseudo_idga_session_id 兩個欄位。工作階段數不建議單純以 COUNT(session) 方式處理,因為背後的計數邏輯涉及使用者的連續互動行為,需透過事件參數還原。GA4 的標準報表中所看到的「工作階段數」,其實是基於「使用者與網站互動的一段連續時間」定義,而這一定義在原始資料中是透過 ga_session_id 參數標記的。每個事件都帶有 ga_session_id,但同一個工作階段可能產生多筆事件,因此直接計算事件列數會導致嚴重的重複計數。正確做法是將每個事件中的 user_pseudo_idga_session_id 配對為一個複合鍵,再針對此複合鍵進行去重計數。

計算不重複使用者數

計算不重複使用者數的 SQL 語法相對直觀。使用者識別碼欄位為 user_pseudo_id,代表 GA4 自動指派給每個瀏覽器或裝置的匿名 ID。這個 ID 是基於瀏覽器 Cookie 或應用程式實例產生的隨機字串,因此不會包含任何可辨識個人身份的資訊。在不重複使用者數的計算中,COUNT(DISTINCT user_pseudo_id) 的作用是對查詢範圍內所有事件對應的使用者 ID 進行去重,只保留唯一值後再計數。若事件資料範圍包含多個日期,由於同一位使用者可能在不同日期都有活動,跨日查詢時此語法也會自動進行全域去重,不會重複計算同一位使用者。若要查詢特定日期範圍內的不重複使用者數,語法如下:

SELECT
  COUNT(DISTINCT user_pseudo_id) AS unique_users
FROM `project_id.analytics_xxxxx.events_20240101`

此語法會回傳單一日期資料表中所有事件所對應的不重複使用者數。若需要查詢多日資料,可使用萬用字元及 _TABLE_SUFFIX 來涵蓋一段日期區間:

SELECT
  COUNT(DISTINCT user_pseudo_id) AS unique_users
FROM `project_id.analytics_xxxxx.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20240101' AND '20240131'

這種寫法能有效控制查詢範圍,也是控制 BigQuery 費用的重要手段。值得注意的是,_TABLE_SUFFIX 比對的是資料表名稱中萬用字元的部分,因此若資料表命名為 events_20240101_TABLE_SUFFIX 即為 20240101,可使用字串函數進行日期區間的比較。

計算工作階段數與互動事件

工作階段數的計算較為複雜。ga_session_id 存放在 event_params 中,而一個工作階段內可能包含多個事件。因此,計算工作階段數須先從每個事件中提取 ga_session_id,再對「(user_pseudo_id, ga_session_id)」組合進行計數:

SELECT
  COUNT(DISTINCT CONCAT(user_pseudo_id, '-', session_id)) AS sessions
FROM (
  SELECT
    user_pseudo_id,
    (SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'ga_session_id') AS session_id
  FROM `project_id.analytics_xxxxx.events_20240101`
)

這裡使用子查詢從 event_params 中取出 ga_session_id,再與使用者 ID 合併成一個複合鍵進行去重計數。此方式可避免同一使用者開啟多個分頁時,工作階段被重複計算的問題。此外,ga_session_id 屬於整數型別的參數,因此取值時需使用 value.int_value。在某些情況下,若 GA4 資源同時跨網域或應用程式追蹤,ga_session_id 的編號仍會在單一使用者層級內唯一,但為了避免跨使用者之間的編號重複,複合鍵的設計仍然必要。

若想進一步瞭解 GA4 BigQuery 在實際 SEO 分析中的應用與報表差異,可參考 GA4 BigQuery 能做什麼?原始事件資料分析與報表差異,內文有更多實務操作情境的說明。

基礎 SQL 語法:查詢事件與流量來源

事件層級的分析是 GA4 BigQuery 的強項之一。透過 SQL,可以精確計算特定事件的發生次數,並從 traffic_source 欄位中提取流量來源與媒介。此處需要熟悉兩個重點:一是使用 WHERE 子句篩選事件名稱與日期,二是透過 UNNEST 展開 event_params 以取得事件層級的參數值。在 GA4 中,每個事件都帶有若干參數,這些參數描述該事件的上下文,例如目前頁面、捲動深度、點擊元素等。透過 SQL 篩選事件名稱可以從海量資料中隔離出特定互動行為,而展開參數則能進一步獲取事件的詳細內容。

拆解 event_params 取得特定參數

page_view 是最常見的互動事件。若要查詢特定日期內「網頁瀏覽」事件的總次數,可使用以下語法:

SELECT
  COUNT(*) AS page_view_count
FROM `project_id.analytics_xxxxx.events_20240101`
WHERE event_name = 'page_view'

此處以 COUNT(*) 計算事件列數,因為每一列在此情況下對應一個 page_view 事件,故不需加上 DISTINCT。若需進一步取得「瀏覽的網址(page_location)」明細,就必須從 event_params 中提取參數。以下語法展示如何同時取得事件名稱、網址與使用者 ID:

SELECT
  user_pseudo_id,
  event_timestamp,
  (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') AS page_url
FROM `project_id.analytics_xxxxx.events_20240101`
WHERE event_name = 'page_view'
LIMIT 1000

此寫法以子查詢配合 UNNEST 的方式,精準取得指定參數值。過程中無需先將整個 event_params 展開,也不會受到其他未使用參數的幹擾。子查詢在每一列中獨立執行,從該列的 event_params 陣列中取出 key 等於 page_location 的記錄,再回傳其 string_value。由於一列事件通常只會有一個同名的參數,這種寫法可以安全地取得唯一值,不需擔心多筆比對結果造成的膨脹。

實務上,page_locationpage_titlesourcemedium 這類字串型參數皆以 value.string_value 儲存,取得方式相同。若要提取數值型別的參數(如 ga_session_idengagement_time_msec),則需改用 value.int_valuevalue.float_value,這點在撰寫查詢時需特別留意。

提取 source/medium 流量來源

GA4 的流量來源資料存放在事件資料表頂層的 traffic_source 欄位。此欄位包含 sourcemediumcampaign 等屬性,代表「使用者最初進入網站時」的來源資訊。查詢時直接透過 traffic_source.sourcetraffic_source.medium 即可取得,不需要 UNNEST

SELECT
  traffic_source.source AS source,
  traffic_source.medium AS medium,
  COUNT(DISTINCT user_pseudo_id) AS users
FROM `project_id.analytics_xxxxx.events_20240101`
GROUP BY source, medium
ORDER BY users DESC

此查詢可列出各來源與媒介組合下的不重複使用者數。需注意的是,traffic_source 代表的是「最初」的來源,而非「當次工作階段」的來源。若要分析每場工作階段實際的來源媒介,則需回到 event_params 中提取 sourcemedium 參數,兩者意義不同,應用時需依分析目的選擇。兩者的比較可歸納為:traffic_source 反映使用者首次進站時的管道,具有「終身歸屬」特性,不會因後續來自其他管道的互動而改變;而 event_params 中的 sourcemedium 則記錄每一場工作階段當下的來源資訊,適合分析使用者回訪時的行為變化。

基礎 SQL 語法:電子商務數據分析

GA4 電子商務資料在 BigQuery 中存放於事件表的 ecommerce 欄位。當網站安裝電子商務增強型事件(如 purchaseadd_to_cartview_item)時,相關交易資料會以結構化格式寫入此欄位。其中,ecommerce.purchase_revenue 儲存交易總金額,items 欄位則存放商品層級的陣列資料。只要查詢 purchase 事件,即可取得購買行為的核心數據。這些事件之所以能正確寫入 ecommerce 欄位,前提是網站前端必須確實安裝 GA4 的電子商務外掛或自訂程式碼,並依照 Google 規範傳送相對應的參數。

計算電子商務收益與購買事件

查詢總收益與購買次數的語法如下。購買事件名稱固定為 purchase,收益則直接取自 ecommerce.purchase_revenue

SELECT
  COUNT(*) AS purchase_count,
  SUM(ecommerce.purchase_revenue) AS total_revenue
FROM `project_id.analytics_xxxxx.events_20240101`
WHERE event_name = 'purchase'

此查詢可快速取得單日購買次數與總收益。若交易含稅金或運費,ecommerce 欄位亦有對應的 tax_valueshipping_value 可供加總。此外,ecommerce 欄位中還包含 transaction_id(交易編號)、currency(貨幣代碼)以及 coupon(優惠券名稱)等欄位,這些資料可協助進行更細緻的訂單分析。若需排除測試交易,可搭配 transaction_id 進行過濾,但這部分需依據商業邏輯而定。

商品層級的資料存放在 items 陣列中,每個商品包含 item_nameitem_idquantityitem_revenue 等子欄位。由於是陣列結構,查詢時必須以 UNNEST(items) 展開。以下範例展示如何計算各商品的銷售數量與收益貢獻:

SELECT
  item.item_name AS product_name,
  SUM(item.quantity) AS qty_sold,
  SUM(item.item_revenue) AS revenue
FROM `project_id.analytics_xxxxx.events_20240101`,
UNNEST(items) AS item
WHERE event_name = 'purchase'
GROUP BY product_name
ORDER BY revenue DESC

此查詢將購買事件中的商品陣列展開,以商品維度彙整銷售數據。若後續要串接廣告費用或站內產品分類資料,可以 item_id 作為關聯鍵。展開 items 陣列後,每一列對應一筆商品記錄,因此同一個購買事件可能產生多列結果,這是在處理商品層級分析時必須理解的特性。另外,item_revenue 欄位代表單一商品的收益貢獻,並非單價,因此在計算商品單價時,可再除以 quantity,但需留意 item_revenue 是否已含折扣。

以下表格整理 GA4 核心指標在 BigQuery 中的查詢邏輯與對應欄位,方便快速查找:

指標名稱 BigQuery 對應欄位/邏輯 常用 SQL 函數
不重複使用者數 使用者層級的去重計數,以 user_pseudo_id 為識別依據 COUNT(DISTINCT user_pseudo_id)
工作階段數 需從 event_params 取出 ga_session_id,與 user_pseudo_id 組合成複合鍵後去重計數 COUNT(DISTINCT CONCAT(...))
事件次數 事件層級的列數計數,直接以 event_name 篩選 COUNT(*)
總收益 ecommerce.purchase_revenue 欄位加總,僅限 purchase 事件 SUM(ecommerce.purchase_revenue)

此表格中的每一列對應一個指標,並列出其實現邏輯與常見的 SQL 函數。使用者在撰寫查詢時,可參考此錶快速確認方向,再依據實際業務需求調整日期範圍與額外條件。

查詢結果整合與後續視覺化應用

BigQuery 的查詢結果能作為各種後續應用的基礎,包含建立例行報表、數據稽覈或客製化儀錶板。由於語法以標準 SQL 撰寫,結果可以直接輸出至 Google Sheets、Colab 或透過 API 串接。最常見的延伸應用是將查詢結果導入 Looker Studio,建立可供團隊共享的視覺化報表。BigQuery 查詢結果的輸出格式與一般資料庫相同,皆為表格結構,因此可以無縫銜接多數分析工具與程式語言。

要以 Looker Studio 連結 BigQuery 資料,基本步驟如下:在 Looker Studio 中新增資料來源,選擇 BigQuery 連接器,接著選取專案與資料表或自訂查詢。若選擇自訂查詢,可將前段撰寫好的 SQL 語法直接貼上,系統會依據查詢結果自動產生欄位。此步驟需確認 BigQuery 專案與 Looker Studio 的帳戶具備足夠的權限,且查詢語法的命名與欄位別名應清楚易懂,以利後續建立報表時的欄位辨識。若使用 View 作為資料來源,則只需在 Looker Studio 中選取該 View,即可直接取用其虛擬資料表結構。

視覺化前,建議先將常用的 SQL 查詢儲存為 BigQuery View。View 不會實際存放資料,但能將複雜的 SQL 語法變成一個虛擬資料表。設定一次後,後續只要更新日期參數或調整維度,不需要在 Looker Studio 中重新編寫語法。這對需要每天更新數據的 SEO 報表來說,能大幅降低維護成本。此外,View 也有助於統一團隊內的指標定義,避免不同成員各自撰寫語法導致口徑不一致。若查詢中使用了固定的日期篩選條件,可在 View 內以參數方式設計,讓後續呼叫時可動態調整。

相較於 GA4 標準報表,透過 SQL 查詢可以更彈性地組合維度與指標,也能處理未經抽樣或彙總的原始事件資料。例如跨使用者歷程的來源比對、頁面層級的互動深度分析,或是與站內搜尋紀錄串接,這些都是標準報表難以達成、但可透過 BigQuery 自行定義的分析項目。標準報表通常只提供預先定義好的維度與指標組合,且大型資料集可能受到抽樣影響;而 BigQuery 中的原始資料則保留每一筆事件,不受抽樣限制,讓分析人員能進行更深入、更貼近實際行為的探索。

為什麼在 BigQuery 查詢 GA4 資料前需要先了解資料表結構?

GA4 匯出至 BigQuery 的原始資料包含巢狀與重複欄位,若不瞭解結構直接查詢,容易導致語法錯誤或計算出錯誤的指標。例如 event_params 必須以 UNNEST 展開才能取得參數值,這是撰寫正確 SQL 的先決條件。巢狀欄位的存在意味著不能用一般扁平資料表的邏輯直接操作,必須先理解欄位之間的包含關係與型別特性,才能正確組合查詢語句。

如何在 BigQuery 中計算 GA4 的不重複使用者數?

可以使用 COUNT(DISTINCT user_pseudo_id) 計算不重複使用者數。user_pseudo_id 是 GA4 用來識別使用者的主要匿名識別碼欄位。DISTINCT 關鍵字確保每一位使用者只被計算一次,即使同一位使用者在查詢範圍內觸發了多筆事件。

GA4 的 event_params 在 BigQuery 中如何查詢?

event_params 在 BigQuery 中屬於重複的巢狀欄位,內部包含 keyvalue 子欄位。查詢時必須使用 UNNEST(event_params) 的寫法將其展開,再配合 WHERE key = '參數名稱' 的條件篩選,最後從對應的 value 型別欄位中取值。需注意不同參數可能存放於不同型別的 value 欄位中,需依實際資料選擇。

如何在 BigQuery 中查詢 GA4 的流量來源?

流量來源資料儲存在 traffic_source 欄位中,可透過 SELECT traffic_source.source, traffic_source.medium 直接提取使用者最初進站的來源與媒介。此欄位屬於頂層欄位,不需要以 UNNEST 處理,直接以點記號存取即可取得來源與媒介名稱。

BigQuery 查詢 GA4 資料有費用限制嗎?

GCP 提供每月 1TB 的免費查詢額度。基礎的 GA4 資料查詢通常不會超過此限制,但若掃描的資料量過大,仍可能產生費用。建議撰寫語法時透過 WHERE 限定日期範圍,或使用 _TABLE_SUFFIX 控制掃描的資料表數量。此外,盡量只選取需要的欄位,避免以 SELECT * 掃描全部欄位,也能有效減少掃描量。

查詢完 GA4 資料後如何進行視覺化?

可以將 BigQuery 的查詢結果連結至 Looker Studio 建立儀錶板。建議先將常用的 SQL 查詢儲存為 BigQuery View,再讓 Looker Studio 以自訂查詢方式連結該 View,可簡化報表設定流程、提升每日更新效率。透過 View 的方式也能集中管理語法,讓團隊成員使用一致的指標定義。

標籤
GA4BigQuerySQL 查詢資料分析事件資料
Eric Chang
Eric Chang
SEO 數據分析與網站量測研究者

以官方文件、實際設定與量測結果整理 SEO 數據、網站追蹤與分析方法,並標示資料來源與判讀限制。