cv.cm

編碼 · 程式語言

SQL 入門教學

閱讀約 10 分鐘

簡答

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 client
SELECT '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%'; -- Ann

JOIN:合併資料表

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 | 2

COUNT(*) 計算列數,而 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 | 2

PostgreSQL、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.50

PostgreSQL、MySQL 與 SQLite 的差異

SQL 的核心是可移植的,但各引擎在細節上有所不同。以下是初學者最先會遇到的差異。

功能PostgreSQLMySQLSQLite
自動遞增 IDGENERATED ALWAYS AS IDENTITY 或 SERIALAUTO_INCREMENTINTEGER PRIMARY KEY
名稱的引號"name"名稱外加反引號"name"
限制筆數LIMIT n 或 FETCH FIRST n ROWS ONLYLIMIT nLIMIT n
字串串接a || bCONCAT(a, b)a || b
布林值真正的 BOOLEAN 型別TINYINT(1)以 0 和 1 儲存
型別檢查嚴格在預設的嚴格模式下為嚴格彈性,除非資料表宣告為 STRICT
UpsertON CONFLICT ... DO UPDATEON DUPLICATE KEY UPDATEON 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 文字中。請使用參數化查詢或預備語句,讓資料庫分別接收查詢與參數值。

全部編碼教學