检查点
Create a new dataset and table to store the data
/ 20
Execute the query to see how many unique products were viewed
/ 15
Execute the query to use the UNNEST() on array field
/ 15
Create a dataset and a table to ingest JSON data
/ 20
Execute the query to COUNT how many racers were there in total
/ 10
Execute the query that will list the total race time for racers whose names begin with R
/ 10
Execute the query to see which runner ran fastest lap time
/ 10
在 BigQuery 使用 JSON、陣列和結構體
GSP416
總覽
BigQuery 是 Google 提供的全代管數據分析資料庫,不但免人工管理,而且價格低廉。您可以使用 BigQuery 查詢 TB 規模的資料,不必管理基礎架構,也不需要資料庫管理員。BigQuery 使用 SQL,並且採用「即付即用」模式。這項服務可讓您專心分析資料,找出有意義的洞察結果。
在本實驗室中,您將深入學習如何在 BigQuery 處理半結構化資料,像是擷取 JSON、陣列資料等。將結構定義反正規化,並製作成有重複欄位的單一巢狀資料表能提升成效,但處理這類陣列資料的 SQL 語法相當複雜。您將練習載入與查詢各種半結構化資料集、排解相關問題,以及解除這類資料的巢狀結構。
學習內容
本實驗室的內容包括:
- 載入、查詢半結構化資料,並解除這類資料的巢狀結構。
- 排解半結構化資料的查詢問題。
設定和需求
點選「Start Lab」按鈕前的須知事項
請詳閱以下操作說明。研究室活動會計時,而且中途無法暫停。點選「Start Lab」 後就會開始計時,讓您瞭解有多少時間可以使用 Google Cloud 資源。
您將在真正的雲端環境中完成實作研究室活動,而不是在模擬或示範環境。為達此目的,我們會提供新的暫時憑證,讓您用來在研究室活動期間登入及存取 Google Cloud。
如要完成這個研究室活動,請先確認:
- 您可以使用標準的網際網路瀏覽器 (Chrome 瀏覽器為佳)。
- 是時候完成研究室活動了!別忘了,活動一開始將無法暫停。
如何開始研究室及登入 Google Cloud 控制台
-
按一下「Start Lab」(開始研究室) 按鈕。如果研究室會產生費用,畫面中會出現選擇付款方式的彈出式視窗。左側的「Lab Details」窗格會顯示下列項目:
- 「Open Google Cloud console」按鈕
- 剩餘時間
- 必須在這個研究室中使用的暫時憑證
- 完成這個實驗室所需的其他資訊 (如有)
-
點選「Open Google Cloud console」;如果使用 Chrome 瀏覽器,也能按一下滑鼠右鍵,然後選取「在無痕式視窗中開啟連結」。
接著,實驗室會啟動相關資源並開啟另一個分頁,當中顯示「登入」頁面。
提示:您可以在不同的視窗中並排開啟分頁。
注意:如果頁面中顯示「選擇帳戶」對話方塊,請點選「使用其他帳戶」。 -
如有必要,請將下方的 Username 貼到「登入」對話方塊。
{{{user_0.username | "Username"}}} 您也可以在「Lab Details」窗格找到 Username。
-
點選「下一步」。
-
複製下方的 Password,並貼到「歡迎使用」對話方塊。
{{{user_0.password | "Password"}}} 您也可以在「Lab Details」窗格找到 Password。
-
點選「下一步」。
重要事項:請務必使用實驗室提供的憑證,而非自己的 Google Cloud 帳戶憑證。 注意:如果使用自己的 Google Cloud 帳戶來進行這個實驗室,可能會產生額外費用。 -
按過後續的所有頁面:
- 接受條款及細則。
- 由於這是臨時帳戶,請勿新增救援選項或雙重驗證機制。
- 請勿申請免費試用。
Google Cloud 控制台稍後會在這個分頁開啟。
開啟 BigQuery 控制台
- 在 Google Cloud 控制台中,依序選取「導覽選單」>「BigQuery」。
接著,畫面中會顯示「歡迎使用 Cloud 控制台中的 BigQuery」訊息方塊,當中會列出快速入門導覽課程指南的連結和版本資訊。
- 點選「完成」。
BigQuery 控制台會隨即開啟。
工作 1:建立新的資料集來儲存資料表
- 在 BigQuery 中,點選專案 ID 旁的三點圖示,然後選取「建立資料集」:
-
將新資料集命名為
fruit_store
,「資料位置」、「預設到期時間」等其他選項保留預設值。 -
點選「建立資料集」。
工作 2:練習處理 SQL 中的陣列
在 SQL 中,每個資料列通常只有一個值,例如下方的水果清單:
Row |
Fruit |
1 |
raspberry |
2 |
blackberry |
3 |
strawberry |
4 |
cherry |
如果想要店內每個人的水果清單呢?結果應如下所示:
Row |
Fruit |
Person |
1 |
raspberry |
sally |
2 |
blackberry |
sally |
3 |
strawberry |
sally |
4 |
cherry |
sally |
5 |
orange |
frederick |
6 |
apple |
frederick |
如果使用傳統關聯資料庫 SQL,當您發現上方資料表的名稱重複,會立即想到要拆分為兩個獨立的資料表:「水果」和「人物」。這個程序稱為正規化,也就是將一個資料表拆分為多個資料表,這是 mySQL 等交易資料庫的常見做法。
就 data warehousing 而言,資料分析師通常採取相反的做法,也就是反正規化,將多個獨立的資料表合併成一個大型報表表格。
接下來,您將學習使用另一種方法,按照不同精細程度,將資料全部儲存在有重複欄位的單一資料表中:
Row |
Fruit (array) |
Person |
1 |
raspberry |
sally |
blackberry | ||
strawberry | ||
cherry | ||
2 |
orange |
frederick |
apple |
前一個資料表有什麼奇怪的地方?
- 只有兩個資料列。
- 單一資料列中有多個 Fruit 欄位值。
- 人物與所有欄位值相關。
發現重點了嗎?這就是 array
資料類型!
這樣更容易解讀 Fruit (array) 資料欄:
Row |
Fruit (array) |
Person |
1 |
[raspberry, blackberry, strawberry, cherry] |
sally |
2 |
[orange, apple] |
frederick |
這兩個資料表的內容完全相同,並呈現兩個重點:
- 陣列只是以方括號 [ ] 表示的項目清單
- BigQuery 會直接顯示「扁平化」的陣列資料,並縱向列出陣列值 (請注意,所有值仍屬於同一列)
現在換您試試看。
- 複製下列查詢,貼到 BigQuery 查詢編輯器:
-
點選「執行」。
-
接下來試著執行下列查詢:
您會收到類似下方的錯誤訊息:
Error: Array elements of types {INT64, STRING} do not have a common supertype at [3:1]
陣列只能使用相同的資料類型,例如全都是字串,或全都是數值。
- 查詢的最終資料表如下:
-
點選「執行」。
-
查看結果後,點選「JSON」分頁標籤,即可瀏覽結果的巢狀結構。
將半結構化 JSON 載入 BigQuery
如果需要將 JSON 檔案擷取至 BigQuery,該怎麼做?
在資料集建立新的資料表 fruit_details
。
- 點選「
fruit_store
」資料集。
現在您會看到「建立資料表」選項。
- 將下列詳細資訊加入資料表:
- 選取來源:在「建立資料表來源」下拉式選單中選取「Google Cloud Storage」。
-
從 Cloud Storage bucket 中選取下列檔案:
cloud-training/data-insights-course/labs/optimizing-for-performance/shopping_cart.json
- 設定檔案格式:設為 JSONL,也就是以換行符號分隔的 JSON
-
將新資料表命名為
fruit_details
。 -
勾選「Schema (Auto detect)」核取方塊。
-
點選「建立資料表」。
請注意,結構定義中的 fruit_array
標為 REPEATED,表示這是陣列。
重點回顧
- BigQuery 直接支援陣列
- 陣列值只能使用相同的資料類型
- 在 BigQuery 中,陣列會以 REPEATED 欄位表示。
點選「Check my progress」確認目標已達成。
工作 3:使用 ARRAY_AGG() 自行建立陣列
如果資料表沒有陣列,您可以自行建立!
- 複製及貼上下列查詢,探索這個公開資料集:
- 點選「執行」並查看結果。
現在請使用 ARRAY_AGG()
函式,將字串值匯總至陣列。
- 複製及貼上下列查詢,探索這個公開資料集:
- 點選「執行」並查看結果
- 接著使用
ARRAY_LENGTH()
函式計算頁數和已查看的產品數:
- 接著將
DISTINCT
加入ARRAY_AGG()
來簡化頁面和產品,瞭解已查看的不重複產品數:
點選「Check my progress」確認目標已達成。
重點回顧
陣列可在許多方面派上用場,例如:
- 使用
ARRAY_LENGTH(<array>)
找出元素數量 - 使用
ARRAY_AGG(DISTINCT <field>)
簡化元素 - 使用
ARRAY_AGG(<field> ORDER BY <field>)
排列元素順序 - 使用
ARRAY_AGG(<field> LIMIT 5)
設定上限
工作 4:查詢包含陣列的資料表
與本課程資料集 data-to-insights.ecommerce.all_sessions
相比,用於 Google Analytics 的 BigQuery 公開資料集 bigquery-public-data.google_analytics_sample
包含更多欄位和資料列。更重要的是,該資料集已將產品、頁面和交易等欄位值,直接儲存為 ARRAY。
- 複製及貼上下列查詢來探索可用的資料,看看是否能找出有重複值的欄位 (陣列):
-
點選「執行」開始查詢。
-
向右捲動結果,直到看到
hits.product.v2ProductName
欄位 (稍後將介紹各欄位別名)。
Google Analytics 結構定義中的可用欄位數量龐大,難以進行分析。
- 請嘗試只查詢造訪和頁面名稱欄位,與先前一樣:
您會收到下列錯誤訊息:
Error: Cannot access field page on a value with type ARRAY<STRUCT<hitNumber INT64, time INT64, hour INT64, ...>> at [3:8]
您必須先將陣列拆分成多個資料列,才能正常查詢 REPEATED 欄位 (陣列)。
舉例來說,hits.page.pageTitle
陣列目前儲存為單一資料列:
並需要拆分成多個資料列:
您該如何使用 SQL 做到這點?
答:在陣列欄位中使用 UNNEST() 函式:
稍後我們會進一步說明 UNNEST(),目前只要瞭解下列幾點即可:
- 需要使用 UNNEST() 陣列,才能以資料列重新呈現陣列元素
- UNNEST() 應一律置於 FROM 子句中的資料表名稱後面,概念上可視為預先彙整的資料表
點選「Check my progress」確認目標已達成。
工作 5:STRUCT 簡介
您可能會好奇,為何欄位別名 hit.page.pageTitle
看起來像是三個欄位合而為一,並以半形句號分隔。除了使用 ARRAY 值靈活「提高」欄位精細程度外,您還能使用一種 SQL 資料類型,將相關欄位歸為一組來「擴大」結構定義。這種資料類型就是 STRUCT (結構體)。
概念上,您可以直接將 STRUCT 視為已預先彙整至主資料表的獨立資料表。
STRUCT 的特點如下:
- 包含一或多個欄位
- 各欄位的資料類型不一定都相同
- 具有別名
聽起來很像資料表,對吧?
探索包含 STRUCT 的資料表
-
請點選「+新增」,然後選取「依據名稱為專案加上星號」並輸入名稱
bigquery-public-data
,即可開啟 bigquery-public-data 資料集。 -
點選「加上星號」。
bigquery-public-data
專案會列在「Explorer」專區。
-
開啟「bigquery-public-data」。
-
找出並開啟「google_analytics_sample」資料集。
-
點選「ga_sessions(366)_」資料表。
-
捲動瀏覽結構定義,使用瀏覽器的尋找功能回答下列問題。
可想而知,現今的電子商務網站需要儲存大量工作階段資料。
在單一資料表設定 32 個 STRUCT 的主要優點,是讓您不需使用 JOIN 函式就能執行下列查詢:
.*
語法會指示 BigQuery 傳回該 STRUCT 的所有欄位,很像是 totals.*
是我們單獨彙整的資料表。將大量報表表格儲存為 STRUCT (預先彙整的「資料表」) 和 ARRAY (提高精細程度) 可以:
- 省去使用 JOIN 函式來彙整資料表 32 次的麻煩,大幅提高效能
- 只在需要時使用 ARRAY 取得精細資料,BigQuery 會將每個資料欄分別儲存在磁碟中
- 將所有業務背景資訊集結在單一資料表中,而不必煩惱如何處理 JOIN 鍵及找出包含所需資料的資料表。
工作 6:練習使用 STRUCT 和陣列
下一個資料集是記錄賽跑選手跑一圈的時間。每一圈稱為「split」。
- 請透過下列查詢嘗試使用 STRUCT 語法,並注意結構體容器內的不同欄位類型:
Row |
runner.name |
runner.split |
1 |
Rudisha |
23.4 |
您從欄位別名發現了什麼?結構體內的欄位呈現巢狀結構 (表示 name 和 split 是 runner 的子集),因此使用點記號標記。
如果選手在單場比賽中有多段時間記錄 (例如每圈時間),該怎麼辦?
當然是使用陣列!
- 執行下列查詢來加以確認:
Row |
runner.name |
runner.splits |
1 |
Rudisha |
23.4 |
26.3 | ||
26.4 | ||
26.1 |
重點回顧:
- 結構體是一種容器,能納入多個欄位名稱和資料類型,並以巢狀結構顯示。
- 陣列可做為結構體內的欄位類型之一,如上方的 split 欄位所示。
練習擷取 JSON 資料
-
建立標題為
racing
的新資料集。 -
依序點選「
racing
」資料集和「建立資料表」。
- 選取來源:在「建立資料表來源」下拉式選單下,選取「Google Cloud Storage」。
-
從 Cloud Storage bucket 中選取下列檔案:
cloud-training/data-insights-course/labs/optimizing-for-performance/race_results.json
- 設定檔案格式:設為 JSONL,也就是以換行符號分隔的 JSON
- 在「結構定義」部分,點選「以文字形式編輯」滑桿,然後新增下列內容:
-
將新資料表命名為
race_results
。 -
點選「建立資料表」。
-
載入工作成功完成後,新建立的資料表結構定義會如下所示:
要怎麼知道哪個欄位屬於 STRUCT?
participants 欄位的資料類型為 RECORD,因此屬於 STRUCT。
哪個欄位屬於 ARRAY?
participants.splits
欄位是父項 participants
結構體內的浮點值陣列,其 REPEATED 模式表示這是陣列。該陣列的值為單一欄位內的多重值,因此屬於巢狀值。
點選「Check my progress」確認目標已達成。
練習查詢巢狀欄位和重複欄位
- 接著來看所有選手的 800 公尺賽跑成績:
系統傳回多少資料列?
答:1 列
如想逐一列出選手名字和參賽類型,該怎麼做?
- 執行下列結構定義,看看結果如何:
Error: Cannot access field name on a value with type ARRAY<STRUCT<name STRING, splits ARRAY<FLOAT64>>>> at [2:27]
就像使用匯總函式時忘記加入 GROUP BY 一樣,這裡的精細程度分成兩個不同等級。一個資料列包含參賽類型,三個資料列包含參賽者名字。該怎麼做,才能將下列資料表...
Row |
race |
participants.name |
1 |
800M |
Rudisha |
2 |
??? |
Makhloufi |
3 |
??? |
Murphy |
...改為:
Row |
race |
participants.name |
1 |
800M |
Rudisha |
2 |
800M |
Makhloufi |
3 |
800M |
Murphy |
如果使用的是傳統關聯 SQL,該如何從參賽類型資料表和參賽者資料表中擷取資訊?方法是使用 JOIN 函式來彙整這兩個資料表。這裡的參賽者 STRUCT (在概念上與資料表非常類似) 已是參賽類型資料表的一部分,但並未與非 STRUCT 欄位「race」正確建立關聯。
您可以使用某項 SQL 指令,將 800 公尺參賽類型與第一個資料表中的各選手名字建立關聯,想想看是哪項指令?
答:CROSS JOIN
答對了!
- 現在試著執行下列指令:
Table name "participants" missing dataset while no default dataset is set in the request
。
雖然參賽者 STRUCT 類似於資料表,實際上仍是 racing.race_results
資料表的欄位。
- 將資料集名稱加入查詢:
- 接著點選「執行」。
太棒了!您成功逐一列出所有參加 800 公尺賽跑的選手!
Row |
race |
name |
1 |
800M |
Rudisha |
2 |
800M |
Makhloufi |
3 |
800M |
Murphy |
4 |
800M |
Bosse |
5 |
800M |
Rotich |
6 |
800M |
Lewandowski |
7 |
800M |
Kipketer |
8 |
800M |
Berian |
- 您還能透過下列方式簡化最後一個查詢:
- 為原始資料表新增別名
- 將字詞「CROSS JOIN」換成半形逗號,半形逗號可間接代表交叉聯結
查詢結果仍相同:
如有多個參賽類型 (800 公尺、100 公尺、200 公尺),是否能使用 CROSS JOIN,直接將各選手名字與每個可能的參賽類型建立關聯,就像計算笛卡爾乘積一樣?
答:不行。這屬於「關聯交叉聯結」,只能拆分與單一資料列相關聯的元素。詳情請參閱 ARRAY 與 STRUCT 使用說明文件。
STRUCT 重點回顧:
- SQL STRUCT 可做為容器來容納其他資料欄位,這些欄位的資料類型不一定相同。「結構體」一詞即代表資料結構。回想先前的例子:
STRUCT(``"Rudisha" as name, [23.4, 26.3, 26.4, 26.1] as splits``)`` AS runner
- STRUCT 具有指定別名 (例如上方的 runner)。概念上可視為主資料表中的資料表。
- 必須先拆分 STRUCT (和 ARRAY),才能使用當中的元素。使用 UNNEST() 括住結構體名稱本身,或結構體中代表陣列的欄位,就能加以拆分及扁平化。
工作 7:實驗室問題:STRUCT()
請使用先前建立的 racing.race_results
資料表,回答下列問題。
工作:編寫查詢,使用 COUNT 函式計算選手總數。
- 請從下列未編寫完成的查詢著手:
FROM
後面做為額外資料來源。參考解法:
Row |
racer_count |
1 |
8 |
答:參賽選手為 8 人。
點選「Check my progress」確認目標已達成。
工作 8:實驗室問題:使用 UNNEST( ) 拆分陣列
請編寫查詢,列出名字開頭為 R 的選手的總賽跑時間,並從時間最短的選手開始排序。您必須使用 UNNEST() 運算子,並從下列未編寫完成的查詢著手。
- 請補全查詢:
- 您必須拆分結構體內的結構體和陣列,放在 FROM 子句後面做為資料來源。
- 務必在適當位置使用別名。
參考解法:
Row |
name |
total_race_time |
1 |
Rudisha |
102.19999999999999 |
2 |
Rotich |
103.6 |
點選「Check my progress」確認目標已達成。
工作 9:篩選陣列值
您發現 800 公尺賽跑的最短單圈時間記錄為 23.2 秒,但不知道是哪位選手留下這項記錄。請建立查詢,找出這名選手。
- 完成未編寫完畢的查詢:
參考解法:
Row |
name |
split_time |
1 |
Kipketer |
23.2 |
點選「Check my progress」確認目標已達成。
恭喜!
您成功擷取 JSON 資料集,透過建立 ARRAY 和 STRUCT,以及拆分半結構化巢狀資料,取得洞察結果。
後續行動/瞭解詳情
- 歡迎進一步閱讀陣列使用說明文件。
- 參考下列實驗室:
Google Cloud 教育訓練與認證
協助您瞭解如何充分運用 Google Cloud 的技術。我們的課程會介紹專業技能和最佳做法,讓您可以快速掌握要領並持續進修。我們提供從基本到進階等級的訓練課程,並有隨選、線上和虛擬課程等選項,方便您抽空參加。認證可協助您驗證及證明自己在 Google Cloud 技術方面的技能和專業知識。
使用手冊上次更新日期:2024 年 2 月 3 日
實驗室上次測試日期:2023 年 8 月 25 日
Copyright 2024 Google LLC 保留所有權利。Google 和 Google 標誌是 Google LLC 的商標,其他公司和產品名稱則有可能是其關聯公司的商標。