一般的資料庫中,我們會看到這樣一個欄位一個值:
如果今天想要紀錄誰吃了哪些水果,則會變成:
這樣便不符合資料庫正規化的原則,因為 name 一直重複地出現。在傳統做法中,我們會很直接地想到拆成兩張表,但在 BigQuery 中有個更快速的方法處理這個問題,用 Array 的方式儲存達到下面這種效果:
雖然看起來跟原本那剛表很像,但它實際上只算 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?
如何下 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 結果都一樣。
以上。
看其他我寫的 BigQuery 文章: