MySQL 学习练习(配套 01 入门资料)

MySQL 学习练习(配套 01 入门资料)

目标:每学完一章就动手练,巩固肌肉记忆。所有练习基于同一个示例库 shop_demo

建议:先自己写 SQL,再看文末参考答案,对比差异。

难度标记:★ 基础 ★★ 进阶 ★★★ 综合

复制代码
编程资源
https://pan.quark.cn/s/7f7c83756948
更多资源
https://pan.quark.cn/s/bda57957c548

准备工作:初始化示例库

把下面整段在 MySQL 里执行一次,得到练习用的 shop_demo 库(含 users / products / orders / order_items 四张表与数据)。

sql 复制代码
DROP DATABASE IF EXISTS shop_demo;
CREATE DATABASE shop_demo DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE shop_demo;

CREATE TABLE users (
    id         INT PRIMARY KEY AUTO_INCREMENT,
    username   VARCHAR(50) NOT NULL,
    city       VARCHAR(50),
    age        TINYINT UNSIGNED,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE products (
    id     INT PRIMARY KEY AUTO_INCREMENT,
    name   VARCHAR(100) NOT NULL,
    price  DECIMAL(10,2) NOT NULL,
    stock  INT NOT NULL DEFAULT 0,
    category VARCHAR(50)
);

CREATE TABLE orders (
    id        INT PRIMARY KEY AUTO_INCREMENT,
    user_id   INT NOT NULL,
    amount    DECIMAL(10,2) NOT NULL,
    status    VARCHAR(20) DEFAULT 'pending',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE order_items (
    id         INT PRIMARY KEY AUTO_INCREMENT,
    order_id   INT NOT NULL,
    product_id INT NOT NULL,
    qty        INT NOT NULL,
    price      DECIMAL(10,2) NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(id),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

INSERT INTO users (username, city, age) VALUES
 ('alice','Beijing',28), ('bob','Shanghai',34), ('carol','Beijing',22),
 ('dave','Shenzhen',41), ('erin','Shanghai',19), ('frank','Beijing',30);

INSERT INTO products (name, price, stock, category) VALUES
 ('Keyboard',199.00,120,'electronics'), ('Mouse',89.50,200,'electronics'),
 ('Notebook',12.00,500,'stationery'), ('Monitor',1299.00,40,'electronics'),
 ('Pen',5.50,1000,'stationery');

INSERT INTO orders (user_id, amount, status) VALUES
 (1,288.50,'paid'), (2,1299.00,'paid'), (1,12.00,'pending'),
 (3,89.50,'paid'), (4,1407.50,'shipped'), (5,199.00,'pending');

INSERT INTO order_items (order_id, product_id, qty, price) VALUES
 (1,1,1,199.00), (1,2,1,89.50),
 (2,4,1,1299.00),
 (3,3,1,12.00),
 (4,2,1,89.50),
 (5,4,1,1299.00), (5,1,1,199.00) /* 注意:order 5 amount 写的是 199,刻意为之,见练习19 */,
 (6,1,1,199.00);

第 3 章 建库建表 ★

练习 1 :创建一个库 test_db,字符集 utf8mb4

练习 2 :在 shop_demo 中新建表 categories(id, name),并给 products 加上外键 category_id 指向它。

练习 3 :给 users 增加列 phone VARCHAR(20),再删除它。


第 5 章 CRUD ★

练习 4 :插入一名用户 gary,城市 Chengdu,年龄 27。

练习 5 :把 bob 的年龄改成 35。

练习 6 :删除用户 frank(注意外键:先处理其订单或临时禁用外键检查)。

练习 7 :查询 products 所有商品,只显示 nameprice


第 6 章 条件查询 ★★

练习 8 :查询年龄 ≥ 30 的用户。

练习 9 :查询城市在 BeijingShanghai 的用户。

练习 10 :查询用户名以 a 开头的用户(LIKE)。

练习 11:查询没有填城市(city 为 NULL)的用户。


第 7 章 排序与分页 ★★

练习 12 :按年龄降序查询用户,取前 3 名。

练习 13:按每页 2 条,查询第 2 页用户(按 id 排序)。


第 8 章 聚合与分组 ★★

练习 14 :统计各城市的用户数量。

练习 15 :统计各城市平均年龄大于 25 的城市及其平均年龄。

练习 16 :计算 productselectronics 类商品的总库存与平均价格。


第 9 章 多表连接 ★★★

练习 17 :列出每笔订单的订单号、下单用户名、金额(orders JOIN users)。

练习 18 :列出每个用户名及其订单数(含 0 单的用户,用 LEFT JOIN + COUNT)。

练习 19 :找出 orders.amount 与实际 order_items 明细金额不一致 的订单(数据质量校验,提示:用子查询或 JOIN + GROUP BY 汇总明细再比较)。

练习 20 :查询买了 Monitor(显示器)的用户名。


第 11 章 索引 ★★

练习 21 :为 orders.user_id 建普通索引,再查看索引列表。

练习 22 :为 users(city, age) 建联合索引,思考哪些查询能命中(最左前缀)。


第 14 章 事务 ★★★

练习 23 :模拟转账------从 users 角度设计一个「积分账户表 wallet(user_id, balance)」,用事务完成 A 给 B 转 100 分,并提交;再演示中途 ROLLBACK 的效果。

练习 24 :故意在事务中写一条会报错的 SQL(如插入重复主键),观察 ROLLBACK 后数据是否回退。


进阶综合 ★★★

练习 25 :统计「已支付(paid)订单」中,每个用户的累计消费金额,按金额降序。

练习 26 :找出销量(order_items.qty 总和)最高的前 3 个商品及其销量。

练习 27 :用 CASE 给用户打标签:age<25「青年」、25--40「中年」、>40「资深」,并统计各标签人数。


参考答案

练习 1

sql 复制代码
CREATE DATABASE test_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

练习 2

sql 复制代码
CREATE TABLE categories (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50));
ALTER TABLE products ADD COLUMN category_id INT;
ALTER TABLE products ADD CONSTRAINT fk_cat
  FOREIGN KEY (category_id) REFERENCES categories(id);

练习 3

sql 复制代码
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;

练习 4

sql 复制代码
INSERT INTO users (username, city, age) VALUES ('gary','Chengdu',27);

练习 5

sql 复制代码
UPDATE users SET age = 35 WHERE username = 'bob';

练习 6(先删子表引用,再删父表)

sql 复制代码
DELETE FROM orders WHERE user_id = (SELECT id FROM users WHERE username='frank');
DELETE FROM users WHERE username = 'frank';
-- 或临时:SET FOREIGN_KEY_CHECKS=0; ... SET FOREIGN_KEY_CHECKS=1;

练习 7

sql 复制代码
SELECT name, price FROM products;

练习 8

sql 复制代码
SELECT * FROM users WHERE age >= 30;

练习 9

sql 复制代码
SELECT * FROM users WHERE city IN ('Beijing','Shanghai');

练习 10

sql 复制代码
SELECT * FROM users WHERE username LIKE 'a%';

练习 11

sql 复制代码
SELECT * FROM users WHERE city IS NULL;

练习 12

sql 复制代码
SELECT * FROM users ORDER BY age DESC LIMIT 3;

练习 13

sql 复制代码
SELECT * FROM users ORDER BY id LIMIT 2, 2;  -- 偏移2,取2条 = 第2页

练习 14

sql 复制代码
SELECT city, COUNT(*) AS cnt FROM users GROUP BY city;

练习 15

sql 复制代码
SELECT city, AVG(age) AS avg_age
FROM users GROUP BY city HAVING AVG(age) > 25;

练习 16

sql 复制代码
SELECT SUM(stock) AS total_stock, AVG(price) AS avg_price
FROM products WHERE category = 'electronics';

练习 17

sql 复制代码
SELECT o.id AS order_no, u.username, o.amount
FROM orders o
JOIN users u ON o.user_id = u.id;

练习 18

sql 复制代码
SELECT u.username, COUNT(o.id) AS order_cnt
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.username;

练习 19(汇总明细比对)

sql 复制代码
SELECT o.id, o.amount, SUM(oi.qty * oi.price) AS detail_sum
FROM orders o
JOIN order_items oi ON o.id = oi.order_id
GROUP BY o.id, o.amount
HAVING o.amount <> SUM(oi.qty * oi.price);
-- 预期命中 order_id = 5(amount 199 vs 明细 1498)

练习 20

sql 复制代码
SELECT DISTINCT u.username
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_items oi ON o.id = oi.order_id
JOIN products p ON oi.product_id = p.id
WHERE p.name = 'Monitor';

练习 21

sql 复制代码
CREATE INDEX idx_user_id ON orders(user_id);
SHOW INDEX FROM orders;

练习 22

sql 复制代码
CREATE INDEX idx_city_age ON users(city, age);
-- 命中:(city) 或 (city, age);不命中:仅 (age)

练习 23

sql 复制代码
CREATE TABLE wallet (user_id INT PRIMARY KEY, balance INT NOT NULL DEFAULT 0);
INSERT INTO wallet VALUES (1, 500), (2, 300);
START TRANSACTION;
UPDATE wallet SET balance = balance - 100 WHERE user_id = 1;
UPDATE wallet SET balance = balance + 100 WHERE user_id = 2;
COMMIT;
-- 中途若执行 ROLLBACK; 则两条 UPDATE 都撤销

练习 24

sql 复制代码
START TRANSACTION;
INSERT INTO users (id, username) VALUES (1, 'dup'); -- 假设 id=1 已存在 → 报错
ROLLBACK; -- 即使前面有成功的语句也会一起回退

练习 25

sql 复制代码
SELECT u.username, SUM(o.amount) AS total
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = 'paid'
GROUP BY u.id, u.username
ORDER BY total DESC;

练习 26

sql 复制代码
SELECT p.name, SUM(oi.qty) AS sales
FROM order_items oi
JOIN products p ON oi.product_id = p.id
GROUP BY p.id, p.name
ORDER BY sales DESC
LIMIT 3;

练习 27

sql 复制代码
SELECT
  CASE WHEN age < 25 THEN '青年'
       WHEN age <= 40 THEN '中年'
       ELSE '资深' END AS tag,
  COUNT(*) AS cnt
FROM users
GROUP BY tag;

自测清单

  • 能独立写出建库、建表、插数据的完整脚本
  • 清楚 WHEREHAVING 的区别
  • 能画出 INNER / LEFT / RIGHT JOIN 的结果差异
  • 理解 UPDATE/DELETE 漏写 WHERE 的后果
  • 能说出索引「最左前缀」是什么
  • 能手写一段转账事务

全部打勾 → 进入 03-精通学习资料.md

复制代码
编程资源
https://pan.quark.cn/s/7f7c83756948
更多资源
https://pan.quark.cn/s/bda57957c548
相关推荐
敲上瘾1 小时前
Redis持久化存储机制:从RDB到AOF再到混合持久化的配置与实践
linux·数据库·redis·缓存
浔溺1 小时前
al+大数据每日学习笔记28
笔记·学习
2401_894915534 小时前
GEO 优化源码全解析:从搜索引擎到 AI 引擎的底层改写逻辑
java·服务器·前端·数据库·人工智能·分布式·搜索引擎
xian_wwq9 小时前
【学习笔记】-深度认知系列-第2讲-大模型到底是什么?——拆解“参数、训练、推理”
笔记·学习·深度认知
正儿八经的少年9 小时前
布隆过滤器(解决redis缓存穿透步骤之一)
数据库·redis·缓存
xrandzj10 小时前
MySQL8.0 从零通关核心操作手册(Ubuntu实战版)
数据库·mysql
我不会插花弄玉10 小时前
4.数据类型【由浅入深-MySQL】
数据库·mysql
lzhdim10 小时前
12、JavaScript常见的内存泄露问题 - JavaScript学习系列文章
开发语言·前端·javascript·学习·ecmascript
denggun1234510 小时前
yield
前端·数据库·python
ltl10 小时前
SQLite Rollback Journal:写前拷贝与提交点
数据库