前幾篇介紹完新增、修改、刪除之後,這篇來講「讀取資料」。
你可能會想:讀取不是最簡單的嗎?為什麼放到後面才講?
其實讀取的操作本身不難,但在實際工作中,讀取資料的時間會比你想像中還要多。
因為你不是單純把資料讀出來就好,通常還需要做各種資料整理——這部分會在下一篇詳細說明。
這篇文章會先帶你了解 SELECT 的基本用法,以及背後的運作原理。
查詢的兩個核心問題:欄位與資料列
想像一個情境:老闆跟你說「我要會員資料」。
光這樣講是不夠的,你還需要知道:
- 要取哪些欄位? 是電話、信箱、還是姓名?
- 要取哪些會員? 是全部會員,還是符合特定條件的會員?
這兩個問題,對應到讀取資料時的兩個核心概念:選擇「欄位」和篩選「資料列」。
「要取哪些欄位」就是在決定要取出電話、信箱、還是姓名,甚至可以是計算後的結果,像是單價乘以數量。
「要取哪些會員」就是在決定要取出全部會員,還是符合某個條件的會員,像是年齡大於 30 歲的人。
在 SQL 中,選擇欄位用 SELECT,篩選資料列用 WHERE,另外還需要用 FROM 指定要從哪張表單取資料。
完整的查詢語法長這樣:
SELECT 欄位或計算結果
FROM 表單名稱
WHERE 篩選條件雖然語法是這樣寫,但資料庫實際執行的順序是 FROM → WHERE → SELECT。
所以接下來我們按照執行順序來說明。
在開始之前,先假設我們有一張「會員」表單:
| ID | 姓 | 名 |
|---|---|---|
| 1 | 王 | 小明 |
| 2 | 李 | 小華 |
接下來的範例都會用這張表單來示範。
FROM:指定要從哪張表單取資料
FROM 後面放的是表單名稱。
這很直覺——你要讀資料,總要告訴資料庫是哪一張表單吧。
FROM 會員WHERE:篩選符合條件的資料列
WHERE 用來設定篩選條件。
只有符合條件的資料列才會被取出來。
FROM 會員
WHERE 姓 = '王'這樣就只會取出姓王的會員。
SELECT:挑出你要的欄位
SELECT 後面放的是你要「取出」的東西。
可以是欄位名稱,也可以是計算結果。
加上 SELECT 之後,就是完整的查詢語法了:
SELECT 姓, 名
FROM 會員
WHERE 姓 = '王'這樣會取出會員表單中,姓王的人的「姓」和「名」兩個欄位。
取出計算結果並取別名
除了取出欄位,SELECT 也可以取出計算後的結果。
例如,我們想把「姓」和「名」串在一起變成「全名」:
SELECT CONCAT(姓, 名) AS 全名
FROM 會員
WHERE 姓 = '王'CONCAT() 是把字串連接起來的函數,括號裡放你要連接的欄位,用逗號隔開。
這裡 CONCAT(姓, 名) 會把「王」和「小明」接在一起,變成「王小明」。
AS 全名 是給這個計算結果取一個別名。
如果不取別名,資料庫會自動產生一個名稱,像是 CONCAT(姓, 名) 或一串看不懂的代號。
取了別名之後,結果的欄位名稱就會顯示成「全名」,清楚易懂。
建議:只要是計算結果,一定要給別名。
為什麼語法順序和執行順序不同?
看完 FROM、WHERE、SELECT 之後,你可能會覺得奇怪:我們思考的順序是「先選表單 → 再篩選 → 最後挑欄位」,但 SQL 的寫法順序卻是 SELECT → FROM → WHERE,剛好相反。
這是因為 SQL 在 1970 年代設計時,目標是讓語法看起來像英文句子,降低使用門檻。
用英文來讀的話:「Select these columns from this table where this condition is true.」
翻成中文就是:「選取這些欄位,從這張表,當滿足這個條件。」
這種結構把「結果」放在最前面,讓你一眼就能看到這個查詢最終要輸出什麼資料。
但資料庫引擎實際執行時,還是按照邏輯順序來處理:
- FROM:先確定要從哪張表抓資料
- WHERE:進行篩選,剔除不符合條件的資料列
- SELECT:從剩下的資料中,挑選出指定的欄位
所以你的思考邏輯是對的,那正是資料庫實際運作的步驟。
這也解釋了一個常見的錯誤:為什麼在 WHERE 中不能使用 SELECT 定義的別名。
SELECT CONCAT(姓, 名) AS 全名
FROM 會員
WHERE 全名 = '王小明' -- 這會報錯!這段語法會報錯。
為什麼?
因為資料庫的執行順序是 FROM → WHERE → SELECT。
當資料庫執行到 WHERE 的時候,SELECT 根本還沒開始處理。
也就是說,CONCAT(姓, 名) AS 全名 這個別名還不存在,資料庫不知道「全名」是什麼。
如果要篩選全名,你必須在 WHERE 中重新寫一次完整的計算:
SELECT CONCAT(姓, 名) AS 全名
FROM 會員
WHERE CONCAT(姓, 名) = '王小明' -- 這樣才對這看起來有點重複,但這就是 SQL 執行順序造成的限制。
WHERE 篩選的運作原理
這裡要解釋一個很重要的概念。
當資料庫執行 WHERE 篩選時,它是這樣運作的:
- 移動到第一筆資料
- 檢查:這筆資料符合條件嗎?符合就留下,不符合就跳過
- 移動到下一筆資料
- 重複步驟 2 和 3,直到檢查完所有資料
- 把所有符合條件的資料回傳
關鍵重點:每次判斷都只看「當下這一筆」資料。
理解這個原理之後,你就會知道為什麼 WHERE 不能用聚合函數。
因為 WHERE 在檢查每一筆資料時,只能看到「當下這一筆」的內容。
它不知道總共有幾筆資料、不知道其他筆的數值是多少,也沒辦法把多筆資料加起來。
所以像 SUM、COUNT、AVG 這類需要「看過全部資料才能算出結果」的運算,在 WHERE 裡面是做不到的。
以下這些運算在 WHERE 中都不能用:
| 運算 | 為什麼不能用 |
|---|---|
| SUM(加總) | 需要把多筆資料加起來 |
| COUNT(計數) | 需要數有幾筆資料 |
| AVG(平均) | 需要把全部加起來再除以筆數 |
| MAX / MIN(最大 / 最小值) | 需要跟其他筆資料比較 |
這些都需要「看到其他筆資料」才能計算,但 WHERE 一次只能看一筆。
那怎麼辦?
這個問題會在「資料整理」的文章解決,那裡會教你用 GROUP BY 和 HAVING。
用 * 和省略 WHERE 取得全部資料
有時候你不需要篩選,就是要全部的資料。
* 是萬用字元,代表「所有欄位」。
省略 WHERE,代表「不篩選,取所有資料列」。
所以當你想把整張表單的資料全部取出來,可以這樣寫:
SELECT *
FROM 會員這會取出會員表單中的所有欄位、所有資料列。
用 Primary Key 查詢特定資料
假設你就是要找 ID 為 1 的那個會員。
建議用 Primary Key(主鍵)來篩選:
SELECT *
FROM 會員
WHERE ID = 1為什麼用 ID 而不是用姓名?
因為可能有兩個人都叫王小明,但 ID 是唯一的。
用 Primary Key 可以確保只會取到那一筆資料。
SELECT 結果作為子查詢(Subquery)
這是一個非常重要的觀念。
當你執行一段 SELECT 查詢後,資料庫會回傳一個結果。
這個結果長什麼樣子?它有欄位名稱、有資料內容,結構上跟一張表單一模一樣。
例如,執行這段查詢:
SELECT ID, CONCAT(姓, 名) AS 全名
FROM 會員
WHERE 姓 = '王'會得到這樣的結果:
| ID | 全名 |
|---|---|
| 1 | 王小明 |
這個結果本身就像是一張「臨時的表單」,只是它沒有被存在資料庫裡,只存在於這次查詢的過程中。
既然它像一張表單,那我們能不能把它當成表單來用?
答案是可以的。這就是「子查詢」的概念。
子查詢放在 FROM
SELECT *
FROM (
SELECT ID, CONCAT(姓, 名) AS 全名
FROM 會員
) AS 臨時表括號裡面的 SELECT 結果,被當成一張表單來使用。
執行結果:
| ID | 全名 |
|---|---|
| 1 | 王小明 |
| 2 | 李小華 |
子查詢放在 WHERE
假設你想找「跟第一個會員同姓的人」:
先看括號裡的子查詢:
SELECT 姓
FROM 會員
WHERE ID = 1執行結果:
| 姓 |
|---|
| 王 |
這個子查詢會回傳「王」這個值。
接著,外層查詢把這個結果拿來當作篩選條件:
SELECT 姓, 名
FROM 會員
WHERE 姓 = (
SELECT 姓
FROM 會員
WHERE ID = 1
)執行結果:
| 姓 | 名 |
|---|---|
| 王 | 小明 |
這裡的邏輯是:
- 先執行括號裡的查詢,取得 ID = 1 的會員的姓(結果是「王」)
- 外層查詢再用這個結果來篩選,找出所有姓王的人
注意:括號裡的查詢要用小括號包起來,表示它是「一包東西」。
小結
這篇文章介紹了 SELECT 的基本用法:
- SELECT:指定要取出的欄位或計算結果,可以用 AS 取別名
- FROM:指定要從哪張表單取資料
- WHERE:設定篩選條件,只取符合條件的資料列
*萬用字元:代表所有欄位- 省略 WHERE:代表取所有資料列
- WHERE 的限制:只能處理單筆資料的判斷,不能用聚合函數
- SELECT 結果是臨時表單:可以被其他查詢引用,放在 FROM 或 WHERE 中
記住這個核心概念:讀取資料就是在回答「要哪些欄位」和「要哪些資料列」這兩個問題。