編碼 · 程式語言
SQL 入門教學
簡答
SQL 是用來在 PostgreSQL、MySQL、SQLite 等關聯式資料庫中建立、查詢與修改資料的標準語言。你只需撰寫 SELECT、INSERT、UPDATE、DELETE 等宣告式語句,由資料庫決定如何執行。本頁介紹資料表設計、篩選、JOIN、分組、子查詢、視窗函式與交易,並列出 PostgreSQL、MySQL 與 SQLite 的主要差異。
SQL 是什麼、可以拿來做什麼
SQL(Structured Query Language,結構化查詢語言)是操作關聯式資料庫的標準語言,資料以列與欄組成的資料表儲存。你描述想要什麼資料,由資料庫引擎決定如何找出來。SQL 由 ISO/IEC 9075 標準化,每個主流資料庫都實作了標準的核心部分,並加上各自的擴充。
開發者、資料分析師、資料工程師與資料庫管理員每天都在用 SQL:儲存應用程式資料、回答業務問題、製作報表,以及供應資料管線。最常見的開源引擎有 PostgreSQL、MySQL(及其分支 MariaDB)與 SQLite;商用的則有 Microsoft SQL Server 和 Oracle Database。
準備資料庫並執行第一個查詢
最簡單的起點是 SQLite,它是不需伺服器、儲存在單一檔案中的資料庫,macOS 與大多數 Linux 系統都已預先安裝。PostgreSQL 與 MySQL 以伺服器形式執行,並附有命令列用戶端 psql 與 mysql。
sqlite3 shop.db # SQLite: opens or creates a file
psql -U postgres -d shop # PostgreSQL client
mysql -u root -p shop # MySQL clientSELECT 'Hello, world!' AS greeting;
-- greeting
-- Hello, world!SQL 關鍵字不分大小寫。常見的慣例是關鍵字用大寫、名稱用小寫,讓查詢更容易閱讀。語句以分號結尾,-- 開頭表示註解。
建立資料表:資料型別與約束
資料表定義會列出欄位、它們的型別,以及確保資料有效的約束。本頁的每個範例都使用 customers 與 orders 兩張資料表。
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name VARCHAR(100) NOT NULL,
city VARCHAR(50)
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id),
amount DECIMAL(10, 2) NOT NULL,
ordered_on DATE NOT NULL
);
INSERT INTO customers (id, name, city) VALUES
(1, 'Ann', 'Oslo'), (2, 'Bo', 'Rome'), (3, 'Cy', 'Oslo');
INSERT INTO orders (id, customer_id, amount, ordered_on) VALUES
(1, 1, 30.00, '2026-01-05'),
(2, 1, 45.50, '2026-02-10'),
(3, 2, 12.00, '2026-02-11');| 型別 | 儲存內容 | 說明 |
|---|---|---|
INTEGER、BIGINT | 整數 | 大型 ID 請用 BIGINT |
DECIMAL(p, s) | 精確的小數 | 用於金額,絕不要用 FLOAT |
VARCHAR(n)、TEXT | 字串 | TEXT 不在標準中,但被廣泛支援 |
DATE、TIMESTAMP | 日期與時間 | 字面值寫成 '2026-01-05' |
BOOLEAN | 真或假 | MySQL 把它視為 TINYINT(1) |
約束包括 PRIMARY KEY(唯一的列識別碼)、NOT NULL、UNIQUE、CHECK (condition),以及 FOREIGN KEY / REFERENCES(該值必須存在於另一張資料表)。字串使用單引號;在標準 SQL 中,雙引號用於識別字。
SELECT:篩選、排序與限制筆數
SELECT 用來讀取資料。WHERE 篩選列,ORDER BY 排序,LIMIT(或標準的 FETCH FIRST n ROWS ONLY)限制回傳的筆數。
SELECT name, city
FROM customers
WHERE city = 'Oslo'
ORDER BY name;
-- Ann | Oslo
-- Cy | Oslo
SELECT id, amount,
CASE WHEN amount >= 40 THEN 'large'
WHEN amount >= 20 THEN 'medium'
ELSE 'small' END AS size
FROM orders
WHERE amount BETWEEN 10 AND 50
ORDER BY amount DESC
LIMIT 2;
-- 2 | 45.50 | large
-- 1 | 30.00 | medium
SELECT name FROM customers WHERE city IS NULL; -- not: city = NULL
SELECT name FROM customers WHERE name LIKE 'A%'; -- AnnJOIN:合併資料表
JOIN 會依條件(通常是外鍵)對兩張資料表的列進行配對。INNER JOIN(或直接寫 JOIN)只保留相符的列;LEFT JOIN 保留左表的所有列,缺少的欄位以 NULL 填補。
SELECT c.name, o.amount
FROM customers AS c
JOIN orders AS o ON o.customer_id = c.id
ORDER BY o.amount DESC;
-- Ann | 45.50
-- Ann | 30.00
-- Bo | 12.00
SELECT c.name, o.id AS order_id
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
WHERE o.id IS NULL; -- customers with no orders
-- Cy | NULL| JOIN | 回傳內容 |
|---|---|
INNER JOIN | 兩張表中都相符的列 |
LEFT JOIN | 左表所有列,加上右表相符的列或 NULL |
RIGHT JOIN | 右表所有列,加上左表相符的列或 NULL |
FULL OUTER JOIN | 兩側所有列;MySQL 不支援 |
CROSS JOIN | 列的所有組合(笛卡兒積) |
GROUP BY 與聚合函式
COUNT、SUM、AVG、MIN、MAX 等聚合函式會把多列彙總成一列。GROUP BY 讓每個群組產生一列結果,HAVING 在聚合之後篩選群組,而 WHERE 則在聚合之前篩選列。
SELECT c.name,
COUNT(o.id) AS orders,
COALESCE(SUM(o.amount), 0) AS total
FROM customers AS c
LEFT JOIN orders AS o ON o.customer_id = c.id
GROUP BY c.id, c.name
ORDER BY total DESC;
-- Ann | 2 | 75.50
-- Bo | 1 | 12.00
-- Cy | 0 | 0
SELECT city, COUNT(*) AS n
FROM customers
GROUP BY city
HAVING COUNT(*) > 1;
-- Oslo | 2COUNT(*) 計算列數,而 COUNT(column) 只計算非 null 的值,這就是上面 Cy 的訂單數為 0 的原因。COALESCE 回傳第一個非 null 的引數。
子查詢、CTE 與視窗函式
子查詢是放在另一個查詢中的查詢。通用資料表運算式(CTE)以 WITH 撰寫,為子查詢命名,讓冗長的查詢更易讀。ROW_NUMBER、RANK 和 SUM(...) OVER 等視窗函式可以跨相關列計算,而不會把它們合併成群組。
SELECT name FROM customers
WHERE id IN (SELECT customer_id FROM orders WHERE amount > 20);
-- Ann
WITH totals AS (
SELECT customer_id, SUM(amount) AS total
FROM orders
GROUP BY customer_id
)
SELECT c.name, t.total,
RANK() OVER (ORDER BY t.total DESC) AS rnk
FROM totals AS t
JOIN customers AS c ON c.id = t.customer_id;
-- Ann | 75.50 | 1
-- Bo | 12.00 | 2PostgreSQL、SQLite 3.25 以上與 MySQL 8.0 以上都支援 CTE 與視窗函式。
修改資料:INSERT、UPDATE、DELETE 與交易
UPDATE 與 DELETE 會影響所有符合 WHERE 子句的列。交易會把多個語句組成一組,讓它們全部成功或全部失敗。
UPDATE customers SET city = 'Paris' WHERE id = 2;
DELETE FROM orders WHERE amount < 15;
BEGIN;
UPDATE orders SET amount = amount - 10 WHERE id = 1;
UPDATE orders SET amount = amount + 10 WHERE id = 2;
COMMIT; -- or ROLLBACK; to undo both
SELECT id, amount FROM orders;
-- 1 | 20.00
-- 2 | 55.50PostgreSQL、MySQL 與 SQLite 的差異
SQL 的核心是可移植的,但各引擎在細節上有所不同。以下是初學者最先會遇到的差異。
| 功能 | PostgreSQL | MySQL | SQLite |
|---|---|---|---|
| 自動遞增 ID | GENERATED ALWAYS AS IDENTITY 或 SERIAL | AUTO_INCREMENT | INTEGER PRIMARY KEY |
| 名稱的引號 | "name" | 名稱外加反引號 | "name" |
| 限制筆數 | LIMIT n 或 FETCH FIRST n ROWS ONLY | LIMIT n | LIMIT n |
| 字串串接 | a || b | CONCAT(a, b) | a || b |
| 布林值 | 真正的 BOOLEAN 型別 | TINYINT(1) | 以 0 和 1 儲存 |
| 型別檢查 | 嚴格 | 在預設的嚴格模式下為嚴格 | 彈性,除非資料表宣告為 STRICT |
| Upsert | ON CONFLICT ... DO UPDATE | ON DUPLICATE KEY UPDATE | ON CONFLICT ... DO UPDATE |
其他差異還包括日期函式(PostgreSQL 與 MySQL 用 NOW(),SQLite 用 datetime('now'))、字串比較是否區分大小寫,以及 MySQL 缺少的完整外部聯結(full outer join)支援。
常見錯誤與後續學習
- 用串接使用者輸入的方式組合查詢,這會造成 SQL injection。請一律使用程式語言提供的參數化查詢(佔位符如
?或$1)。 - 在分組查詢中選取既不在
GROUP BY中、也不在聚合函式內的欄位。 - 金額使用
FLOAT而不是DECIMAL。 - 忘了為 JOIN 與篩選用到的欄位建立索引;
CREATE INDEX idx_orders_customer ON orders(customer_id);與EXPLAIN在這方面很有幫助。 - 以為沒有
ORDER BY時資料列會以固定順序回傳。
想進一步學習,請閱讀官方手冊:PostgreSQL 教學、MySQL Reference Manual 與 SQLite 的 SQL 語言參考。也可以用真實資料集練習:先用白話寫出問題,再把每個問題轉成查詢。
常見問題
SQL 是程式語言嗎?
SQL 是宣告式查詢語言:你描述想要的結果,而不是計算結果的步驟。PL/pgSQL 與 T-SQL 等廠商擴充則加入了變數、迴圈與預存程序。
初學者該從哪個資料庫開始?
SQLite 不需要伺服器,非常適合學習。PostgreSQL 是學習符合標準、可用於實際應用的 SQL 的好選擇,而 MySQL 在網站主機服務中很常見。
WHERE 和 HAVING 有什麼差別?
WHERE 在分組之前篩選個別的列,HAVING 則在計算聚合函式之後篩選群組。
INNER JOIN 和 LEFT JOIN 有什麼差別?
INNER JOIN 只回傳兩張表中都有相符的列。LEFT JOIN 回傳左表的所有列,沒有相符時右表的欄位以 NULL 填補。
如何防止 SQL injection?
絕不要把使用者輸入直接放進 SQL 文字中。請使用參數化查詢或預備語句,讓資料庫分別接收查詢與參數值。