BigQuery 的巢狀結構 Array / Struct

Pei Lee
5 min readMar 30, 2019

--

一般的資料庫中,我們會看到這樣一個欄位一個值:

圖片來源:QwikLabs

如果今天想要紀錄誰吃了哪些水果,則會變成:

圖片來源:QwikLabs

這樣便不符合資料庫正規化的原則,因為 name 一直重複地出現。在傳統做法中,我們會很直接地想到拆成兩張表,但在 BigQuery 中有個更快速的方法處理這個問題,用 Array 的方式儲存達到下面這種效果:

圖片來源:QwikLabs

雖然看起來跟原本那剛表很像,但它實際上只算 2 個 Row。它的概念比較像是這樣的狀況:

Array

嘗試看看這樣的 Query:

#standardSQL
SELECT ['raspberry', 'blackberry', 'strawberry', 'cherry'] AS fruit_array

得到的結果會像是這樣 1 個 Row:

如果要把它變成多個 Row 則要使用 UNNEST():

#standardSQL
SELECT *
FROM UNNEST(['raspberry', 'blackberry', 'strawberry', 'cherry'])

UNNEST():把 Array 轉成 Table,所以 UNNEST() 是放在 FROM 後面的。

然而,Array 裡面每個元素都必須是相同的資料型態。如果需要儲存不同的資料型態,則需要使用 STRUCT ()。

STRUCT

試試這樣的 Query:

#standardSQL 
SELECT STRUCT("Rudisha" as name, 23.4 as split) as runner

可以同時存在 STRING 以及 FLOAT:

另外,也可以放入 Array:

#standardSQL 
SELECT STRUCT("Rudisha" as name, [23.4, 26.3, 26.4, 26.1] as splits) AS runner

如何知道一個欄位是 STRUCT 還是 ARRAY?

STRUCT:Type =RECORD, Mode = REPEATED,能容納不同型態。

ARRAY:Mode = REPEATED,只能容納單一型態如上圖的 FLOAT。

如何 Query 這樣的 Table?

table name: race_results

如何下 Query 執行出這樣的結果:

先想像假設今天是傳統的資料庫,我們可能會去 JOIN race 這張表以及 participants 這張表,今天不一樣的只是 participants 是一個 STRUCT 的物件,但我們仍可以把 STRUCT 視為 Table 中的 Table,因此可以用 CROSS JOIN:

#standardSQL 
SELECT race, participants.name
FROM racing.race_results
CROSS JOIN race_results.participants

注意 participants 還是隸屬於 race_results 這張表下的,因此要將 table name 也打上去。有個更簡潔的寫法:

#standardSQL 
SELECT race, participants.name
FROM racing.race_results AS r, r.participants

用「,」代替 CROSS JOIN。

不論是 Array 或是 Struct,在進行操作前一定要先攤平裡面的元素。

如何找到名字是 R 開頭的選手,並將總成績由好到壞排序?

#standardSQL
SELECT
p.name,
SUM(split_times) as total_race_time
FROM racing.race_results AS r
, UNNEST(r.participants) AS p
, UNNEST(p.splits) AS split_times
WHERE p.name LIKE "R%"
GROUP BY p.name
ORDER BY total_race_time;

實際測過,發現有沒有加 UNNEST 結果都一樣。

以上。

--

--

Pei Lee
Pei Lee

Written by Pei Lee

Data Team Lead @ Retail & E-commerce Industry

No responses yet