Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

網站會不定期發佈技術筆記、職場心得相關的內容,歡迎關注本站!

網站
首頁關於我部落格
部落格
分類系列文

© 新人日誌. All rights reserved. 2020-present.

跨表單查詢:用 FROM 合併兩張表(排列組合)

最後更新:2026年2月2日資料庫

上一篇我們學了「先計算」的做法,把子查詢放在 WHERE 裡面。

這篇要來學「先合併」的做法,把兩張表合併成一張大表,再做計算。

我們會從一個很笨、很暴力的方式開始,讓你理解合併的底層原理。

FROM 後面放兩張表會發生什麼事?

之前我們學的查詢,FROM 後面都只放一張表:

SELECT * FROM 會員

但其實 FROM 後面可以放很多張表,用逗號隔開:

SELECT * FROM 會員, 訂單

這樣寫的意思是:「我要從會員表和訂單表撈資料」。

聽起來很直覺對吧?

但這樣寫會發生一件事:排列組合。

當你寫 FROM 會員, 訂單 的時候,資料庫會把兩張表的資料做完整的排列組合。

假設會員表有 3 筆資料(John、Mary、Tom),訂單表也有 3 筆資料。

資料庫會把每一個會員配上每一筆訂單:

會員ID姓名訂單ID訂單.會員ID金額
1John1160,000
1John2120,000
1John3255,000
2Mary1160,000
2Mary2120,000
2Mary3255,000
3Tom1160,000
3Tom2120,000
3Tom3255,000
姓名John
訂單ID1
訂單.會員ID1
金額60,000
姓名John
訂單ID2
訂單.會員ID1
金額20,000
姓名John
訂單ID3
訂單.會員ID2
金額55,000
姓名Mary
訂單ID1
訂單.會員ID1
金額60,000
姓名Mary
訂單ID2
訂單.會員ID1
金額20,000
姓名Mary
訂單ID3
訂單.會員ID2
金額55,000
姓名Tom
訂單ID1
訂單.會員ID1
金額60,000
姓名Tom
訂單ID2
訂單.會員ID1
金額20,000
姓名Tom
訂單ID3
訂單.會員ID2
金額55,000

結果是 3 × 3 = 9 筆資料。

這在數學上叫做「笛卡爾積」(Cartesian Product),就是把兩邊所有的元素做完整的排列組合。

為什麼會這樣?

因為你沒有告訴資料庫「合併條件」。

資料庫不知道「John 的訂單是哪幾筆」,所以它只好把所有可能的組合都列出來。

加上合併條件:用 WHERE 篩選

現在我們有了一張 9 筆資料的大表,但裡面有很多「配錯」的組合。

例如「John 配上 Mary 的訂單」,這是不對的。

怎麼辦?用 WHERE 篩選出正確的組合:

SELECT *
FROM 會員, 訂單
WHERE 會員.會員ID = 訂單.會員ID

這個條件會篩選出「會員表的會員 ID 等於訂單表的會員 ID」的資料列。

我們來看看 9 筆資料中,哪些符合這個條件:

會員ID姓名訂單ID訂單.會員ID金額滿足合併條件?
1John1160,000✓
1John2120,000✓
1John3255,000
2Mary1160,000
2Mary2120,000
2Mary3255,000✓
3Tom1160,000
3Tom2120,000
3Tom3255,000
姓名John
訂單ID1
訂單.會員ID1
金額60,000
滿足合併條件?✓
姓名John
訂單ID2
訂單.會員ID1
金額20,000
滿足合併條件?✓
姓名John
訂單ID3
訂單.會員ID2
金額55,000
滿足合併條件?
姓名Mary
訂單ID1
訂單.會員ID1
金額60,000
滿足合併條件?
姓名Mary
訂單ID2
訂單.會員ID1
金額20,000
滿足合併條件?
姓名Mary
訂單ID3
訂單.會員ID2
金額55,000
滿足合併條件?✓
姓名Tom
訂單ID1
訂單.會員ID1
金額60,000
滿足合併條件?
姓名Tom
訂單ID2
訂單.會員ID1
金額20,000
滿足合併條件?
姓名Tom
訂單ID3
訂單.會員ID2
金額55,000
滿足合併條件?

只有「會員ID = 訂單.會員ID」的那 3 筆打勾。

篩選後的結果:

會員ID姓名訂單ID訂單.會員ID金額
1John1160,000
1John2120,000
2Mary3255,000
姓名John
訂單ID1
訂單.會員ID1
金額60,000
姓名John
訂單ID2
訂單.會員ID1
金額20,000
姓名Mary
訂單ID3
訂單.會員ID2
金額55,000

這就是我們想要的合併結果!

用合併後的表計算平均客單價

現在我們會合併表了,接下來把任務做完。

老闆說:「幫我找出平均客單價 5 萬以上的會員,然後給我他們的信箱。」

第一步:合併兩張表,並計算平均客單價

SELECT *,
    AVG(金額) OVER (PARTITION BY 會員.會員ID) AS 客單價
FROM 會員, 訂單
WHERE 會員.會員ID = 訂單.會員ID

這裡用了窗函數 OVER (PARTITION BY ...) 來計算每個會員的平均客單價。

執行後會得到一張包含「客單價」欄位的大表:

會員ID姓名訂單ID金額客單價
1John160,00040,000
1John220,00040,000
2Mary355,00055,000
姓名John
訂單ID1
金額60,000
客單價40,000
姓名John
訂單ID2
金額20,000
客單價40,000
姓名Mary
訂單ID3
金額55,000
客單價55,000

你會發現 John 的兩筆訂單,客單價都是 40,000(因為是平均值)。

第二步:篩選出平均客單價超過 5 萬的會員

現在我們有了一張包含客單價的表,接下來要篩選出客單價超過 5 萬的會員。

第一步的查詢產生了一張暫時性表格,我們可以把它放進 FROM 後面:

SELECT 姓名, 信箱
FROM (第一步的查詢) AS 會員_訂單
WHERE 客單價 > 50000

這裡的 AS 會員_訂單 是幫這張暫時性表格取一個名字。

SQL 規定子查詢放在 FROM 後面時,一定要取別名,不然會報錯。

完整的查詢

把第一步的查詢帶入:

SELECT 姓名, 信箱
FROM (
    SELECT *,
        AVG(金額) OVER (PARTITION BY 會員.會員ID) AS 客單價
    FROM 會員, 訂單
    WHERE 會員.會員ID = 訂單.會員ID
) AS 會員_訂單
WHERE 客單價 > 50000

執行後會得到:

姓名信箱
Marymary@mail.com
信箱mary@mail.com

任務完成!

如果老闆加條件呢?

老闆突然說:「等一下,單筆金額 2 萬 5 以下的訂單不要算進去,我只想看大單的平均客單價。」

意思是:計算平均客單價的時候,要先排除掉金額太小的訂單。

第一步:合併兩張表,並排除小單

以我們的資料為例,哪些訂單符合條件?

會員ID姓名訂單ID訂單.會員ID金額滿足合併條件?金額 >= 25000?
1John1160,000✓✓
1John2120,000✓
1John3255,000✓
2Mary1160,000✓
2Mary2120,000
2Mary3255,000✓✓
3Tom1160,000✓
3Tom2120,000
3Tom3255,000✓
姓名John
訂單ID1
訂單.會員ID1
金額60,000
滿足合併條件?✓
金額 >= 25000?✓
姓名John
訂單ID2
訂單.會員ID1
金額20,000
滿足合併條件?✓
金額 >= 25000?
姓名John
訂單ID3
訂單.會員ID2
金額55,000
滿足合併條件?
金額 >= 25000?✓
姓名Mary
訂單ID1
訂單.會員ID1
金額60,000
滿足合併條件?
金額 >= 25000?✓
姓名Mary
訂單ID2
訂單.會員ID1
金額20,000
滿足合併條件?
金額 >= 25000?
姓名Mary
訂單ID3
訂單.會員ID2
金額55,000
滿足合併條件?✓
金額 >= 25000?✓
姓名Tom
訂單ID1
訂單.會員ID1
金額60,000
滿足合併條件?
金額 >= 25000?✓
姓名Tom
訂單ID2
訂單.會員ID1
金額20,000
滿足合併條件?
金額 >= 25000?
姓名Tom
訂單ID3
訂單.會員ID2
金額55,000
滿足合併條件?
金額 >= 25000?✓

現在 WHERE 要同時滿足兩個條件:滿足合併條件 AND 金額 >= 25000。

只有第 1 筆和第 6 筆兩個條件都打勾。

John 的第二筆訂單(20,000)雖然符合合併條件,但金額太小被排除了。

篩選後的結果:

會員ID姓名訂單ID訂單.會員ID金額
1John1160,000
2Mary3255,000
姓名John
訂單ID1
訂單.會員ID1
金額60,000
姓名Mary
訂單ID3
訂單.會員ID2
金額55,000

接下來計算平均客單價。

因為 John 只剩一筆訂單,平均客單價就是 60,000(不是之前的 40,000)。

Mary 也是一筆,平均客單價是 55,000。

計算後的結果:

會員ID姓名訂單ID金額客單價
1John160,00060,000
2Mary355,00055,000
姓名John
訂單ID1
金額60,000
客單價60,000
姓名Mary
訂單ID3
金額55,000
客單價55,000

第二步:篩選出平均客單價超過 5 萬的會員

現在兩個人的客單價都超過 5 萬了!

所以最後的結果會有兩個人:John 和 Mary。

完整的查詢:

SELECT 姓名, 信箱
FROM (
    SELECT *,
        AVG(金額) OVER (PARTITION BY 會員.會員ID) AS 客單價
    FROM 會員, 訂單
    WHERE 會員.會員ID = 訂單.會員ID
      AND 訂單.金額 >= 25000
) AS 會員_訂單
WHERE 客單價 > 50000

執行後會得到:

姓名信箱
Johnjohn@mail.com
Marymary@mail.com
信箱john@mail.com
信箱mary@mail.com

結果是對的,但你有沒有發現一個問題?

這個寫法有點醜

看一下 WHERE 的部分:

WHERE 會員.會員ID = 訂單.會員ID
  AND 訂單.金額 >= 25000

這裡混在一起的有兩種條件:

  1. 合併條件:會員.會員ID = 訂單.會員ID — 決定兩張表怎麼接在一起
  2. 篩選條件:訂單.金額 >= 25000 — 決定要保留哪些資料

這兩種條件的「目的」完全不同,但現在都塞在同一個 WHERE 裡面。

當條件越來越多的時候,問題就來了。

想像一下,如果老闆又加了更多條件:

WHERE 會員.會員ID = 訂單.會員ID
  AND 訂單.金額 >= 25000
  AND 會員.註冊日期 >= '2024-01-01'
  AND 訂單.訂單日期 >= '2024-06-01'
  AND 會員.等級 = 'VIP'

你要從這一堆 AND 裡面分辨:

  • 哪個是合併條件?
  • 哪個是篩選條件?

未來要修改或維護的時候,想到就覺得很痛苦。

我們需要一個專門的合併指令

如果能把「合併條件」和「篩選條件」分開寫,程式碼會更清楚:

  • 合併的歸合併
  • 篩選的歸篩選

這就是為什麼我們需要學 JOIN 指令。

小結

這篇學到的重點:

  • FROM 表A, 表B 會產生排列組合(笛卡爾積)
  • 用 WHERE 加上合併條件,可以篩選出正確的組合
  • 這個方法可行,但合併條件和篩選條件混在一起,不好維護
  • 下一篇會學 JOIN 指令,讓合併條件和篩選條件分開

記住一件重要的事:JOIN 指令底層做的事情,跟這篇的笨方法一模一樣。

都是先排列組合,再用條件篩選。

只是 JOIN 讓你寫起來更清楚、更好維護而已。

下一篇,我們來學真正的 JOIN 語法!

目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

資料庫

目錄

  • FROM 後面放兩張表會發生什麼事?
  • 加上合併條件:用 WHERE 篩選
  • 用合併後的表計算平均客單價
  • 第一步:合併兩張表,並計算平均客單價
  • 第二步:篩選出平均客單價超過 5 萬的會員
  • 完整的查詢
  • 如果老闆加條件呢?
  • 第一步:合併兩張表,並排除小單
  • 第二步:篩選出平均客單價超過 5 萬的會員
  • 這個寫法有點醜
  • 我們需要一個專門的合併指令
  • 小結