SQL 快速入門
概述
本文將以 SQLite 方言為例,介紹 SQL 的基本要點。我們先建立幾個示例表並填充資料,這些表將作為後續各節示例的基礎。讀完本概述後,你將擁有一個可以上手操作的簡單模式(schema),並瞭解如何為常見的 SQL 操作準備好環境。
-- Create a table for users
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
username TEXT NOT NULL,
email TEXT NOT NULL,
joined_date TEXT NOT NULL -- stored as ISO-8601 strings in SQLite
);
-- Create a table for orders
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
product TEXT NOT NULL,
quantity INTEGER NOT NULL,
order_date TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Insert sample data into users
INSERT INTO users (username, email, joined_date) VALUES
('alice', 'alice@example.com', '2023-01-15'),
('bob', 'bob@example.com', '2023-02-10'),
('carol', 'carol@example.com', '2023-03-05');
-- Insert sample data into orders
INSERT INTO orders (user_id, product, quantity, order_date) VALUES
(1, 'Keyboard', 2, '2023-04-01'),
(1, 'Mouse', 1, '2023-04-07'),
(2, 'Monitor', 1, '2023-04-15'),
(3, 'Desk Lamp', 3, '2023-05-02'),
(2, 'USB Cable', 5, '2023-05-10');
這兩個表 —— users 和 orders —— 將作為我們查詢的基礎。你可以在 SQLite 環境中(例如 sqlite3 命令列工具或任意圖形介面工具)執行這些語句,以便動手試驗後面的示例。
常用語法
本節介紹四種最常用的 SQL 語句:
- SELECT
- UPDATE
- DELETE
- INSERT
我們只關注每種語句的典型用法,不會涵蓋所有可能的選項或關鍵字。按照慣例,所有 SQL 關鍵字都以大寫形式書寫,這是一個好習慣。不過對 SQL 引擎來說,關鍵字通常不區分大小寫。
1. SELECT
SELECT 語句用於從一個或多個表中檢索資料。最簡單的形式是指定所需的列以及要從中檢索的表:
SELECT id, username, email
FROM users;
如果需要所有列,使用 *:
SELECT *
FROM orders;
可以使用 WHERE 篩選結果:
SELECT username, email
FROM users
WHERE joined_date >= '2023-02-01';
要對結果排序,使用 ORDER BY:
SELECT id, product, quantity, order_date
FROM orders
WHERE quantity > 1
ORDER BY order_date DESC;
限制返回的行數:
SELECT *
FROM orders
LIMIT 3;
組合多個子句:
SELECT username, email
FROM users
WHERE username LIKE 'a%'
ORDER BY joined_date;
這裡的 LIKE 關鍵字匹配所有以 a 開頭的名稱。
2. UPDATE
使用 UPDATE 修改已有的行。務必加上 WHERE 子句,以免無意中更新了所有行:
-- Change Bob's email address
UPDATE users
SET email = 'bob.new@example.com'
WHERE username = 'bob';
可以一次更新多個列:
UPDATE users
SET email = 'alice.updated@example.com',
joined_date = '2023-01-20'
WHERE username = 'alice';
好的做法是先用相同的 WHERE 執行一次 SELECT:
SELECT * FROM users WHERE username = 'alice';
-- Review the row, then:
UPDATE users
SET email = 'alice.updated@example.com'
WHERE username = 'alice';
3. DELETE
DELETE 語句從表中刪除行。同樣,WHERE 子句必不可少:
-- Delete Carol's record
DELETE FROM users
WHERE username = 'carol';
刪除表中的所有行(但保留表結構):
DELETE FROM orders;
SQLite 支援不帶 WHERE 的 DELETE FROM table_name; 來刪除每一行。如果只想刪除特定的行:
DELETE FROM orders
WHERE order_date < '2023-04-01';
4. INSERT
INSERT 向表中新增新行。我們在概述中已經用它填充了 users 和 orders。基本語法需要指定列和值:
INSERT INTO users (username, email, joined_date)
VALUES ('dave', 'dave@example.com', '2023-06-01');
如果按確切順序為每一列都提供了值,可以省略列列表:
INSERT INTO orders VALUES (NULL, 4, 'Webcam', 2, '2023-06-02');
-- NULL for id since it's an AUTOINCREMENT primary key
如果要從另一個表插入資料(這是在 INSERT 中使用 SELECT 的一種簡單形式):
INSERT INTO orders (user_id, product, quantity, order_date)
SELECT id, 'Headphones', 1, '2023-06-05'
FROM users
WHERE username = 'bob';
技巧
上面的示例涵蓋了基本的 CRUD 操作。本節重點介紹 SQLite 中更高階但也很常用的 SELECT 模式,包括隱式連線、顯式連線、公用表表示式(WITH)和子查詢。當你需要合併多個表中的資料或得出聚合結果時,這些技巧尤其有用。
1. 在 FROM 中使用多個表的隱式連線
隱式連線(有時稱為“逗號連線”)在 FROM 子句中列出多個表,並在 WHERE 中指定連線條件。這種寫法簡潔,但在複雜查詢中可能不夠清晰:
-- List every order along with the username of the user who placed it
SELECT u.username, o.product, o.quantity, o.order_date
FROM users u, orders o
WHERE u.id = o.user_id;
這裡,u 是 users 的別名,o 是 orders 的別名。連線條件 u.id = o.user_id 將 users 中的行與 orders 中匹配的行關聯起來。
你可以新增篩選條件:
-- Only orders with quantity > 1
SELECT u.username, o.product, o.quantity
FROM users u, orders o
WHERE u.id = o.user_id
AND o.quantity > 1
ORDER BY o.order_date;
2. 顯式連線語法(INNER JOIN、LEFT JOIN)
SQLite 支援標準的顯式 JOIN 語法,為了清晰起見,推薦使用這種寫法:
-- INNER JOIN retrieves only matching rows
SELECT u.username, o.product, o.order_date
FROM users u
INNER JOIN orders o
ON u.id = o.user_id;
左連線(或左外連線)返回左表中的所有行,以及右表中匹配的行(如果有):
-- List all users, including those who have not placed orders
SELECT u.username, o.product, o.order_date
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id;
如果某個使用者沒有匹配的 orders,則 product 和 order_date 列將為 NULL。
多重連線 當涉及兩個以上的表時,可以將連線串聯起來:
-- Suppose we add a table for payment info
CREATE TABLE IF NOT EXISTS payments (
id INTEGER PRIMARY KEY,
order_id INTEGER NOT NULL,
amount REAL NOT NULL,
payment_date TEXT NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id)
);
-- Sample data
INSERT INTO payments (order_id, amount, payment_date) VALUES
(1, 49.98, '2023-04-02'),
(2, 19.99, '2023-04-08'),
(3, 149.99, '2023-04-16');
-- Query: find username, product, and payment amount for each paid order
SELECT u.username,
o.product,
p.amount,
p.payment_date
FROM users u
INNER JOIN orders o
ON u.id = o.user_id
INNER JOIN payments p
ON o.id = p.order_id
ORDER BY p.payment_date;
3. WITH 子句(公用表表示式)
由 WITH 定義的公用表表示式(CTE)可以建立一個臨時的、具名的結果集,供後續查詢引用。這有助於把複雜查詢拆分成更易讀的若干部分。
-- Example: Find users who have spent more than $50 in total
WITH user_totals AS (
SELECT u.id AS user_id,
u.username,
SUM(p.amount) AS total_spent
FROM users u
INNER JOIN orders o
ON u.id = o.user_id
INNER JOIN payments p
ON o.id = p.order_id
GROUP BY u.id, u.username
)
SELECT username, total_spent
FROM user_totals
WHERE total_spent > 50;
在這個示例中:
- 名為
user_totals的 CTE 彙總了每個使用者的總消費。 - 外層
SELECT根據彙總後的消費額篩選使用者。
CTE 也可以是遞迴的,但作為快速入門,請先專注於非遞迴 CTE。
4. 子查詢
子查詢(也稱為內部查詢或巢狀查詢)是嵌入在其他 SQL 語句中的查詢。它們可以出現在 SELECT 列表、FROM 子句或 WHERE 子句中。
4.1 SELECT 列表中的子查詢
-- Add a column showing the number of orders each user has placed
SELECT u.username,
u.email,
(
SELECT COUNT(*)
FROM orders o
WHERE o.user_id = u.id
) AS order_count
FROM users u;
子查詢 (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) 會針對 users 中的每一行執行一次。
4.2 WHERE 子句中的子查詢
-- Find users who have never placed an order
SELECT username, email
FROM users
WHERE id NOT IN (
SELECT DISTINCT user_id
FROM orders
);
也可以改用 NOT EXISTS:
SELECT username, email
FROM users u
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.user_id = u.id
);
4.3 FROM 子句中的子查詢
-- Join users to a derived table that calculates order counts
SELECT t.username, t.order_count
FROM (
SELECT u.id AS user_id,
u.username,
COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id
GROUP BY u.id, u.username
) AS t
WHERE t.order_count >= 2;
這裡,FROM 子句中的子查詢(別名為 t)計算每個使用者的訂單數,外層查詢再按 order_count 進行篩選。
5. 聚合與 GROUP BY
嚴格來說,聚合也屬於 SELECT 模式的一部分,但它實在太常用,值得單獨介紹:
-- Total quantity ordered per product
SELECT product,
SUM(quantity) AS total_quantity
FROM orders
GROUP BY product
ORDER BY total_quantity DESC;
HAVING
要對聚合值進行篩選,應使用 HAVING 而不是 WHERE:
-- Only products with total quantity > 2
SELECT product,
SUM(quantity) AS total_quantity
FROM orders
GROUP BY product
HAVING total_quantity > 2;
6. 綜合運用
下面將其中幾種技巧組合成一個更完整的示例:
- 列出每個使用者、其訂單總數、訂購總數量以及最近一次下單日期。
- 只包含至少下過一個訂單的使用者。
- 按訂購總數量降序排列。
WITH user_orders AS (
SELECT u.id AS user_id,
u.username,
COUNT(o.id) AS order_count,
SUM(o.quantity) AS total_quantity,
MAX(o.order_date) AS last_order_date
FROM users u
INNER JOIN orders o
ON u.id = o.user_id
GROUP BY u.id, u.username
)
SELECT username,
order_count,
total_quantity,
last_order_date
FROM user_orders
WHERE order_count >= 1
ORDER BY total_quantity DESC;
說明:
- CTE
user_orders按使用者彙總與訂單相關的指標。 - 外層查詢過濾掉訂單數為零的使用者,並對結果排序。
付諸實踐
下面是一些額外的示例和變體,在使用 SQLite 進行日常資料分析時可能會用到:
總消費排名前 3 的使用者 結合 payments 和 orders 對使用者進行排名。
WITH spending AS ( SELECT u.id AS user_id, u.username, SUM(p.amount) AS total_spent FROM users u INNER JOIN orders o ON u.id = o.user_id INNER JOIN payments p ON o.id = p.order_id GROUP BY u.id, u.username ) SELECT username, total_spent FROM spending ORDER BY total_spent DESC LIMIT 3;尋找沒有付款記錄的訂單 使用左連線,並在付款一側尋找
NULL。SELECT o.id AS order_id, u.username, o.product, o.order_date FROM orders o LEFT JOIN payments p ON o.id = p.order_id INNER JOIN users u ON o.user_id = u.id WHERE p.id IS NULL;通過子查詢更新 假設我們想在一個新列中把某個日期之前加入的使用者標記為“legacy”。首先新增一列:
ALTER TABLE users ADD COLUMN status TEXT DEFAULT 'active'; UPDATE users SET status = 'legacy' WHERE joined_date < '2023-02-01';基於關聯條件刪除 如果需要刪除已取消訂單的付款記錄(假設存在一個
orders.status = 'canceled'列),可以這樣寫:DELETE FROM payments WHERE order_id IN ( SELECT id FROM orders WHERE status = 'canceled' );巢狀 CTE 串聯多個 CTE 來分解一個複雜問題:
WITH recent_orders AS ( SELECT * FROM orders WHERE order_date >= '2023-04-01' ), recent_payments AS ( SELECT o.id AS order_id, o.user_id, p.amount, p.payment_date FROM recent_orders o INNER JOIN payments p ON o.id = p.order_id ) SELECT u.username, SUM(rp.amount) AS recent_spending FROM users u INNER JOIN recent_payments rp ON u.id = rp.user_id GROUP BY u.username ORDER BY recent_spending DESC;
結語
本文以 SQLite 方言介紹了 SQL 的基礎知識,重點講解了最常用的語句 —— SELECT、UPDATE、DELETE 和 INSERT —— 以及若干實用的 SELECT 技巧。現在你已經有了包含 users、orders 和 payments 的模式可供試驗,並且瞭解瞭如何:
- 建立表並插入資料。
- 執行簡單的 CRUD 操作。
- 以隱式和顯式方式連線表。
- 使用公用表表示式(
WITH)。 - 在語句的不同部分編寫子查詢。
- 聚合資料並使用
HAVING進行篩選。
這些示例應能為日常資料分析任務打下堅實的基礎。隨著你越來越熟練,可以進一步探索 SQLite 中的視窗函式、觸發器和索引策略等高階主題。