上一篇文章,我們從五道 SQL 基礎題開始,練習如何計算每月營收、找出高消費顧客、分析各國購買人數、找出從未下單的會員,以及計算不同訂單狀態的占比。
如果你有看完上篇,應該已經發現,SQL 面試並不是看到題目就馬上開始寫 Query。真正重要的,是先確認商業問題、資料格式與指標定義,再決定查詢條件應該怎麼下。
這篇文章接著分享第 6~10 題,會練習日期處理、平均值、HAVING、跨表 JOIN 與 CASE WHEN。SQL 範例延續上篇,以 Amazon Athena 語法為主。
練習使用的資料表
我們同樣使用兩張簡化的電商資料表:
customers 是客戶資料表,包含 customer_id、signup_date、country、acquisition_channel。
orders 是訂單紀錄表,包含 order_id、customer_id、order_date、order_status、total_amount。其中 order_date 以 YYYYMMDD 的整數格式儲存,例如 20250831。
第 6 題:如何找出每位顧客的第一次購買日期?
看到「第一次購買日期」,你是不是會很直覺地想到:依照 customer_id 分組,再使用 MIN(order_date) 找出最早的日期?
SELECT
customer_id,
MIN(order_date) AS first_order_date
FROM orders
GROUP BY customer_id;這個方向沒有錯,但先別急著把它當成最後答案。題目說的是「第一次購買」,而不是「第一次建立訂單」,兩者不一定是同一件事。
下手之前,你可以先問:
1. 第一次購買是第一次建立訂單、第一次付款成功,還是第一次完成訂單?取消或付款失敗的訂單要算嗎?
2. order_date 的資料格式是什麼?如果它是 YYYYMMDD 的整數,能不能直接使用日期函數?
3. 如果顧客完成訂單後又全額退款,這筆訂單還算他的第一次購買嗎?
4. 同一位顧客是否可能有多個 customer_id?如果有,第一次購買日期要以帳號還是真實顧客為單位?
假設面試官告訴你,第一次購買是指第一筆已完成的訂單,而且 order_date 是 YYYYMMDD 的整數格式,那麼在 Athena 中要先將整數轉成日期,再找出最小值:
SELECT
customer_id,
MIN(
CAST(
DATE_PARSE(CAST(order_date AS VARCHAR), '%Y%m%d')
AS DATE
)
) AS first_purchase_date
FROM orders
WHERE order_status = 'completed'
GROUP BY customer_id;這題看似只是在考 MIN,其實也在測試你是否能分辨「下單」與「購買」的差異,以及能不能正確處理日期格式。
第 7 題:如何計算每位顧客的平均訂單金額?
平均訂單金額看起來很簡單,把 total_amount 放進 AVG() 不就好了嗎?
SELECT
customer_id,
AVG(total_amount) AS average_order_value
FROM orders
GROUP BY customer_id;但平均值是 SQL 面試裡很容易出現陷阱的地方,因為你一定要先確認每一列資料代表什麼。
你可以先問:
1. orders 的一列就是一張訂單嗎?還是一張訂單中的一項商品?如果一張訂單有三項商品,會不會出現三列?
2. 平均訂單金額只計算 completed 訂單嗎?取消、付款失敗與退款訂單要不要排除?
3. total_amount 是整張訂單的最終金額,還是單項商品金額?是否已經扣除折扣與退款?
4. 題目想看的是每位顧客自己的平均訂單金額,還是全公司的平均客單價?分析粒度不同,答案也會不同。
假設一列就是一張訂單,total_amount 是最終訂單金額,而且只計算已完成的訂單,那麼可以寫成:
SELECT
customer_id,
COUNT(DISTINCT order_id) AS order_count,
SUM(total_amount) AS total_spend,
AVG(total_amount) AS average_order_value
FROM orders
WHERE order_status = 'completed'
GROUP BY customer_id;除了平均訂單金額,我也會一起列出訂單數與累積消費金額。為什麼呢?因為平均消費 3,000 元的顧客,可能只買過一次,也可能已經買過十次。只看平均值,很容易忽略背後完全不同的顧客行為。
第 8 題:如何找出購買超過 3 次的顧客?
這一題常被拿來測試 WHERE 和 HAVING 的差別。很多人看到「超過 3 次」,第一個反應可能是把條件寫在 WHERE:
SELECT
customer_id,
COUNT(DISTINCT order_id) AS order_count
FROM orders
WHERE COUNT(DISTINCT order_id) > 3
GROUP BY customer_id;但這段 SQL 無法正確執行,因為 WHERE 是在分組與彙總之前篩選原始資料,COUNT() 則是分組後才會得到的結果。如果要篩選彙總後的訂單數,就需要使用 HAVING。
不過,語法問題解決後,商業定義還是要確認:
1. 購買超過 3 次,是指至少 3 次,還是大於 3 次,也就是 4 次以上?
2. 一次購買要用資料列數量、order_id 數量,還是 completed 訂單數來判斷?
3. 是否有指定觀察期間?是歷史累積購買次數,還是最近一年、最近 90 天的購買次數?
4. 退貨或全額退款的訂單,還算一次購買嗎?
假設題目的意思是歷史上完成 4 張以上訂單的顧客,那麼 Query 可以寫成:
SELECT
customer_id,
COUNT(DISTINCT order_id) AS order_count
FROM orders
WHERE order_status = 'completed'
GROUP BY customer_id
HAVING COUNT(DISTINCT order_id) > 3
ORDER BY order_count DESC;發現了嗎?WHERE 和 HAVING 都是在篩選資料,但它們發生的時間點不同。WHERE 先篩選哪些訂單可以進入計算,HAVING 再篩選哪些顧客的彙總結果符合條件。
第 9 題:如何比較不同顧客來源渠道的營收?
這題開始更接近真實的商業分析。公司想知道哪個 acquisition_channel 帶來最多營收,你可能會把 customers 和 orders 連接起來,再依照渠道加總 total_amount。
SELECT
c.acquisition_channel,
SUM(o.total_amount) AS revenue
FROM customers c
JOIN orders o
ON c.customer_id = o.customer_id
GROUP BY c.acquisition_channel
ORDER BY revenue DESC;這段 SQL 可以算出結果,但看到結果後,真的就能判斷哪個渠道表現最好嗎?先想一下,還有哪些問題需要釐清:
1. acquisition_channel 代表顧客第一次接觸、第一次註冊,還是最後一次轉換的渠道?歸因規則是什麼?
2. 營收是否只計算已完成的訂單?退款、折扣與不同幣別應該如何處理?
3. customers 中的 customer_id 是否唯一?如果同一位顧客在 customers 表中出現多列,JOIN 後會不會讓營收被重複計算?
4. 題目想比較的是營收,還是渠道效益?如果要評估效益,是否還需要廣告費、顧客取得成本與毛利資料?
假設 acquisition_channel 是顧客註冊時的來源渠道、customer_id 在 customers 中不重複,而且只計算已完成訂單,那麼可以寫成:
SELECT
c.acquisition_channel,
COUNT(DISTINCT c.customer_id) AS purchasing_customers,
COUNT(DISTINCT o.order_id) AS order_count,
SUM(o.total_amount) AS revenue
FROM customers c
JOIN orders o
ON c.customer_id = o.customer_id
WHERE o.order_status = 'completed'
GROUP BY c.acquisition_channel
ORDER BY revenue DESC;我會同時保留購買顧客數、訂單數與營收,因為只看營收很難解釋渠道差異。某個渠道的營收較高,可能只是帶來的顧客比較多,不代表每位顧客的價值更高。
而且別忘了,營收最高也不等於投資報酬率最高。如果真的要決定行銷預算怎麼分配,還需要搭配渠道成本、CAC、毛利與 LTV,才能做出完整判斷。
第 10 題:如何將訂單分成高、中、低金額?
最後一題要使用 CASE WHEN 進行分類。假設高金額訂單是 3,000 元以上、中金額訂單是 1,000~2,999 元,其餘則是低金額訂單,你可能會寫成:
SELECT
order_id,
total_amount,
CASE
WHEN total_amount >= 3000 THEN 'High'
WHEN total_amount >= 1000 THEN 'Medium'
ELSE 'Low'
END AS order_value_segment
FROM orders;語法看起來很直覺,但面試官很可能會繼續問你:「為什麼是 1,000 元和 3,000 元?」
所以,開始分類前,你可以先確認:
1. 高、中、低金額的門檻是公司已經定義好的,還是需要你根據資料分布自行決定?
2. total_amount 是否為最終付款金額?取消、退款與折扣後的訂單該如何分類?
3. 如果 total_amount 是 NULL、負數或異常高值,要放在哪一類?還是應該先排除並檢查?
4. 不同國家或產品的訂單金額差異很大時,是否應該使用相同門檻?
假設門檻已由公司定義,並且只分類已完成、金額不為 NULL 的訂單,那麼 Query 可以寫成:
SELECT
order_id,
total_amount,
CASE
WHEN total_amount >= 3000 THEN 'High'
WHEN total_amount >= 1000 THEN 'Medium'
ELSE 'Low'
END AS order_value_segment
FROM orders
WHERE order_status = 'completed'
AND total_amount IS NOT NULL;CASE WHEN 會從上到下判斷條件,因此條件順序也很重要。如果先寫 total_amount >= 1000,那麼 3,000 元以上的訂單也會先被歸類為 Medium,後面的 High 條件就永遠不會執行。
實務上,如果公司沒有提供門檻,我不會直接憑感覺設定數字,而是先查看訂單金額分布,或依照百分位數、毛利與顧客經營策略來決定分類方式。
總結:真正加分的是你如何拆解問題
完成這五道題目後,你會發現,SQL 語法只是解題過程中的一部分。
第 6 題看起來在考 MIN,實際上要先定義第一次購買;第 7 題使用 AVG,卻必須先確認資料粒度;第 8 題考 WHERE 和 HAVING,也要先釐清購買次數;第 9 題使用 JOIN 分析渠道營收,但不能把營收直接當成渠道效益;第 10 題使用 CASE WHEN 分群,門檻也不能毫無依據地設定。
發現了嗎?同一段 SQL,在不同的資料結構與商業定義下,可能會得到完全不同的答案。
所以下次面試時,先別急著證明自己寫 Query 的速度。先說明你想確認哪些事情、你目前採用什麼假設,以及這些假設會如何影響結果。這些看似繞了一點的步驟,反而更能讓面試官看見你真正的分析能力。
數據分析師的價值,不只是把資料查出來,而是確保查出來的資料,真的能回答原本的商業問題。
看完答案後,別忘了真的動手寫一次
看到這裡,連同上篇:SQL 面試題入門:數據分析師必會的 10 道基礎題 (上),你已經完成 10 道 SQL 基礎面試題了。
不過,看懂答案和面試時能夠自己拆解問題,還是兩件不太一樣的事情。真正遇到 SQL 面試題時,面試官通常不只想知道你的 Query 能不能執行,也會觀察你是否知道要先確認資料格式、指標定義與商業情境。
如果你想繼續練習,我整理了一份《電商產業 SQL 面試實戰題庫》,收錄 24 道不與本篇重複的電商案例題。
題目會從基礎到進階,帶你練習付款、退款、營收、新舊客、商品表現、成長率、優惠券與購物漏斗等常見分析情境,並提供 Athena SQL 與 MySQL 8.0 參考答案,這裏購買下載:https://buymeacoffee.com/aderlabsharon/e/572913
最後也提供讀者專屬折扣碼:BLOG,結帳時輸入折扣碼即可享有9折優惠~
Buy me a coffee 用行動支持我的內容創作
如果文章對你有幫助,歡迎用小額贊助支持 Sharon 持續分享數據分析實務。



