跨引擎查詢練習(附解答)
驗收自己:先判斷這是哪一種 operation,再想這個引擎怎麼寫。ClickHouse、Spark、MongoDB 實作練習附解答——數 NULL、array 成員判斷、分群等等。
驗收自己:讀完情境,先想「這是哪一種 operation」,再想「這個引擎怎麼寫」,最後才展開對答案。範例 schema 以 analytics.events 為主,欄位視情境而定。
ClickHouse
1. 只算有填 email 的 sign-up(排除 NULL)
情境: events 有 signup_email 欄位,部分為 NULL。同時算出總列數與有 email 的列數。
顯示解答
SELECT count(*) AS total, count(signup_email) AS with_email
FROM analytics.events;
count(*)含 NULL、count(col)只算非 NULL;兩者的差就是缺 email 的數量。↳ count(*) vs count(欄位)
2. 找出活動名稱含 “summer”(不分大小寫)
情境: 在 field_name 裡搜尋子字串 summer。
顯示解答
SELECT * FROM analytics.events
WHERE field_name ILIKE '%summer%';
ILIKE= 不分大小寫的LIKE;%代表任意字元。↳ LIKE / ILIKE 子字串搜尋
3. 篩出 tags 陣列含 ‘vip’ 的列
情境: tags 是 Array(String),要判斷有 vip 成員在 tags 裡面的資料集。
顯示解答
SELECT * FROM analytics.events
WHERE has(tags, 'vip');
has(arr, val)就是 ClickHouse 的 array 成員判斷。↳ ClickHouse 陣列函式
4. 找出「至少一個 latency 超過 500ms」的請求
情境: latencies 是數值 array,只要有一個 > 500 就要。
顯示解答
SELECT request_id FROM analytics.events
WHERE arrayExists(x -> x > 500, latencies);
arrayExists等於 Python 的any()。↳ ClickHouse 陣列函式
5. 依消費與活躍度分群
情境: 用多層條件把使用者分成不同 segment。
過濾欄位:total_spent and days_active
VIP_Active: total_spent >= 5000 & days_active <= 14VIP_At_Risk: total_spent >= 5000Loyal_Regular: total_spent >= 1000- 其他皆列為
Standard即可
顯示解答
SELECT user_id,
multiIf(
total_spent >= 5000 AND days_active <= 14, 'VIP_Active',
total_spent >= 5000, 'VIP_At_Risk',
total_spent >= 1000, 'Loyal_Regular',
'Standard'
) AS segment
FROM analytics.events;
multiIf是精簡版的CASE WHEN。↳ ClickHouse 陣列函式
6. Email 為 Null 時,填入 unknown
情境: signup_email 部分為 NULL,輸出時把 NULL 換成字串 unknown。
顯示解答
SELECT ifNull(signup_email, 'unknown') AS email
FROM analytics.events;
ClickHouse 用
ifNull給 null 預設值(Spark 是NVL)。↳ ClickHouse 陣列函式
Spark
1. 每個 customer 的商品清單(含重複 vs 去重)
情境: 把多列 customer_id 的 item_bought field 聚成 array → 一欄可以有重複、另一欄不能有重複。所以會有三欄: id, all_items, unique_items。
顯示解答
SELECT customer_id,
collect_list(item_bought) AS all_items,
collect_set(item_bought) AS unique_items
FROM store_sales
GROUP BY customer_id;
collect_list保留重複、collect_set去重。↳ Spark 陣列函式與 collect
2. 篩出 tags 含 ‘vip’ 的列
顯示解答
SELECT * FROM events
WHERE array_contains(tags, 'vip');
Spark 的 array 成員判斷是
array_contains。↳ Spark 陣列函式與 collect
3. 把本地價乘上匯率
情境: prices 是數值 array,逐一乘上 rate。
顯示解答
SELECT transform(prices, x -> x * rate) AS usd_prices
FROM orders;
transform(arr, x -> ...)對每個元素套運算式。↳ Spark 陣列函式與 collect
4. 只留下超過 500ms 的 latency
顯示解答
SELECT filter(latencies, x -> x > 500) AS slow
FROM events;
WHERE篩的是「整列」,filter篩的是「同一列 array 內的元素」,層級不同:想留下「有任一 latency > 500 的列」用WHERE exists(latencies, x -> x > 500)(但整個 array 原封不動);想把 array 裡 ≤ 500 的元素砍掉,只能用filter。
filter(arr, x -> ...)保留符合 predicate 的元素。↳ Spark 陣列函式與 collect
5. email 為 null 時填 ‘unknown’
顯示解答
SELECT NVL(signup_email, 'unknown') AS email FROM events;
Spark 用
NVL給 null 預設值(ClickHouse 是ifNull)。↳ Spark 陣列函式與 collect
MongoDB
1. 年齡介於 20 到 40(不含端點)
2. name 含 “abc”(不分大小寫)
顯示解答
db.users.find({ name: { $regex: "abc", $options: "i" } });
$regex≈ SQLLIKE、$options: "i"≈ILIKE。↳ MongoDB 運算子與詞彙
3. 有填 email 的 document
顯示解答
db.users.find({ email: { $exists: true } });
Mongo 裡 Absent ≠ null:
$exists問的是「有沒有這個 key」。↳ MongoDB 運算子與詞彙
4. type 是 a / b / c 之一 — 等於 IN
5. 未成年 或 VIP
顯示解答
db.users.find({ $or: [{ age: { $lt: 18 } }, { vip: true }] });
$or/$and/$nor吃一個 array。↳ MongoDB 運算子與詞彙
參考來源
- ClickHouse SQL Reference — https://clickhouse.com/docs/en/sql-reference
- Spark SQL Built-in Functions — https://spark.apache.org/docs/latest/api/sql/
- MongoDB Query Operators — https://www.mongodb.com/docs/manual/reference/operator/query/
Related: 回到跨引擎資料庫查詢總覽——這些答案背後的深入篇:count(*) vs count(欄位)、LIKE / ILIKE、ClickHouse 陣列函式、Spark 陣列函式、MongoDB 運算子。