最后,把课程内容串成从创建 Table 到查询统计的完整流程。场景仍是在线商店,重点不是构建复杂系统,而是看见 CREATE TABLE、INSERT、SELECT、WHERE、JOIN、GROUP BY、UPDATE 和 DELETE 如何合作。
01 · DESIGN — 设计结构
02 · CREATE — 创建 Table
03 · INSERT — 加入数据
04 · SELECT — 确认内容
05 · WHERE — 筛选 Row
06 · JOIN — 连接关系
07 · GROUP — 统计分组
08 · UPDATE — 安全修改
09 · DELETE — 谨慎删除
10 · VERIFY — 验证结果
01创建四张 Table
先建立 users、products、orders 与 order_items。此时没有业务数据,但主键、必填值、默认状态和外键关系已经定义。
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(200) NOT NULL UNIQUE
);
CREATE TABLE products (
id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
category VARCHAR(100)
);
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT NOT NULL,
order_date DATE NOT NULL,
status VARCHAR(50) DEFAULT 'pending',
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE order_items (
id INT PRIMARY KEY,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(id),
FOREIGN KEY (product_id) REFERENCES products(id)
);02插入测试数据
先插入被引用的 users 与 products,再插入 orders,最后插入依赖订单和商品的 order_items。这个顺序让每条外键在写入时都能找到目标。
INSERT INTO users (id, name, email) VALUES
(1, 'Alice', '[email protected]'),
(2, 'Bob', '[email protected]'),
(3, 'Chris', '[email protected]');
INSERT INTO products (id, name, price, category) VALUES
(1, 'Keyboard', 80.00, 'Electronics'),
(2, 'Mouse', 40.00, 'Electronics'),
(3, 'Coffee Mug', 15.00, 'Home'),
(4, 'Backpack', 60.00, 'Fashion');
INSERT INTO orders (id, user_id, order_date, status) VALUES
(1001, 1, '2026-08-01', 'paid'),
(1002, 2, '2026-08-02', 'pending');
INSERT INTO order_items (id, order_id, product_id, quantity) VALUES
(1, 1001, 1, 1),
(2, 1001, 2, 2),
(3, 1002, 4, 1);03先查看基础数据
导入或初始化后,简单 SELECT 能快速确认预期 Row 是否存在。这一步虽然基础,却能在继续写复杂查询前暴露错误。
SELECT * FROM users;
SELECT * FROM products;
SELECT * FROM orders;
SELECT * FROM order_items;04筛选并排序商品
只看价格至少 50 的商品,并从高到低排序。SELECT 决定显示的 Column,WHERE 决定保留的 Row,ORDER BY 决定显示顺序。
SELECT name, price
FROM products
WHERE price >= 50
ORDER BY price DESC;| name | price |
|---|---|
| Keyboard | 80.00 |
| Backpack | 60.00 |
05查询完整订单信息
直接查看 order_items 只得到 ID。沿着外键 JOIN users、orders、order_items 和 products,结果就能回答谁买了什么以及数量。
SELECT o.id AS order_id, u.name AS user_name,
p.name AS product_name, oi.quantity, p.price
FROM orders AS o
INNER JOIN users AS u ON o.user_id = u.id
INNER JOIN order_items AS oi ON o.id = oi.order_id
INNER JOIN products AS p ON oi.product_id = p.id
ORDER BY o.id;06计算每笔订单总额
每条明细金额是 quantity × price,再按订单分组求 SUM。订单 1001 中 Keyboard 为 80 × 1,Mouse 为 40 × 2,总额 160。
| 商品 | 数量 | 单价 | 小计 |
|---|---|---|---|
| Keyboard | 1 | 80 | 80 |
| Mouse | 2 | 40 | 80 |
| 订单总额 | 160 |
SELECT o.id AS order_id, u.name AS user_name,
SUM(oi.quantity * p.price) AS total_amount
FROM orders AS o
INNER JOIN users AS u ON o.user_id = u.id
INNER JOIN order_items AS oi ON o.id = oi.order_id
INNER JOIN products AS p ON oi.product_id = p.id
GROUP BY o.id, u.name;07统计每位用户的订单数
LEFT JOIN 保留没有订单的 Chris。COUNT(o.id) 忽略 NULL,因此 Chris 显示 0,而不是错误地数出一条。
SELECT u.name, COUNT(o.id) AS order_count
FROM users AS u
LEFT JOIN orders AS o ON u.id = o.user_id
GROUP BY u.id, u.name
ORDER BY order_count DESC;08安全修改订单状态
订单 1002 完成支付时,先 SELECT 确认目标,再 UPDATE,最后用相同条件复查。这就是“先查、再改、再查”的安全闭环。
SELECT * FROM orders WHERE id = 1002;
UPDATE orders
SET status = 'paid'
WHERE id = 1002;
SELECT * FROM orders WHERE id = 1002;09在一处修改商品价格
Mouse 调价到 45 时只修改 products 的一条 Row。商品资料只维护一份,之后的查询会读取新价格。仍然先确认 id 2 是 Mouse,再修改并验证。
SELECT * FROM products WHERE id = 2;
UPDATE products SET price = 45.00 WHERE id = 2;
SELECT * FROM products WHERE id = 2;10谨慎删除测试数据
删除未被引用的测试商品同样先查、再删、再查。若商品已被 order_items 引用,外键可能拒绝删除;这正是数据库在保护关系完整性。
SELECT * FROM products WHERE id = 3;
DELETE FROM products WHERE id = 3;
SELECT * FROM products WHERE id = 3;不是每个问题都需要整条流程。 最贵的三个商品只需 SELECT、ORDER BY 和 LIMIT;按用户统计订单需要 JOIN、COUNT 和 GROUP BY;改变订单状态只需带精确 WHERE 的 UPDATE。根据问题选择所需部件。
你已经建立起 SQL 的问题解决路径
真正重要的不是背下所有命令,而是能判断:数据在哪张 Table、需要哪些 Column、是否要筛选、是否要连接其他 Table、最终是读取还是修改。这个思路会继续支持你学习子查询、CTE、Window Function、Index 与 Transaction。