1. MySQL SQL基础练习的必要性
对于任何想从事数据库相关工作或提升数据处理能力的人来说,MySQL SQL基础练习都是必经之路。我见过太多人把"会写SQL"挂在嘴边,但面对实际业务需求时却无从下手。基础不牢,地动山摇——这句话在SQL学习上体现得尤为明显。
为什么100道基础练习题如此重要?因为SQL的复杂性不在于语法本身,而在于如何将业务需求准确转化为查询逻辑。这100道题就像围棋中的定式,通过反复练习能培养出对数据关系的直觉判断。我在带团队时发现,那些能快速解决复杂查询问题的人,无一例外都有扎实的基础练习功底。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与数据初始化
2.1 MySQL安装配置要点
虽然题目是练习题,但真实的操作环境能极大提升学习效果。推荐使用MySQL 8.0+版本,它在窗口函数、CTE等现代SQL特性上支持更好。安装时注意:
bash复制# Ubuntu安装示例
sudo apt update
sudo apt install mysql-server
sudo mysql_secure_installation
Windows用户可以从MySQL官网下载安装包,安装时特别注意:
- 选择"Developer Default"配置
- 设置root密码复杂度至少8位含大小写和特殊字符
- 端口保持默认3306,但生产环境一定要修改
重要提示:安装完成后务必创建专用练习用户,不要直接用root操作:
sql复制CREATE USER 'sql_learner'@'localhost' IDENTIFIED BY 'Practice@123'; GRANT ALL PRIVILEGES ON *.* TO 'sql_learner'@'localhost'; FLUSH PRIVILEGES;
2.2 练习数据库设计
我设计了一套包含6个表的电商数据库模型,覆盖了SQL练习所需的各种场景:
sql复制CREATE DATABASE sql_practice;
USE sql_practice;
-- 用户表
CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
join_date DATETIME DEFAULT CURRENT_TIMESTAMP,
vip_level ENUM('普通','白银','黄金','铂金') DEFAULT '普通'
);
-- 商品表
CREATE TABLE products (
product_id INT PRIMARY KEY AUTO_INCREMENT,
product_name VARCHAR(100) NOT NULL,
category_id INT,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE
);
-- 订单表(核心练习表)
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
order_date DATETIME DEFAULT CURRENT_TIMESTAMP,
total_amount DECIMAL(12,2),
status ENUM('待支付','已支付','已发货','已完成','已取消') DEFAULT '待支付',
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
-- 订单明细表
CREATE TABLE order_items (
item_id INT PRIMARY KEY AUTO_INCREMENT,
order_id INT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL DEFAULT 1,
unit_price DECIMAL(10,2) NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(order_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
-- 商品分类表
CREATE TABLE categories (
category_id INT PRIMARY KEY AUTO_INCREMENT,
category_name VARCHAR(50) NOT NULL,
parent_id INT NULL
);
-- 用户地址表
CREATE TABLE user_addresses (
address_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
is_default BOOLEAN DEFAULT FALSE,
province VARCHAR(50),
city VARCHAR(50),
district VARCHAR(50),
detail_address VARCHAR(200),
FOREIGN KEY (user_id) REFERENCES users(user_id)
);
这套模型包含了:
- 基础CRUD操作
- 多表关联查询
- 聚合函数使用
- 子查询应用
- 事务处理场景
- 复杂业务逻辑实现
3. 基础查询专项训练
3.1 单表查询核心20题
- 查询所有商品信息(基础SELECT)
sql复制SELECT * FROM products;
- 查询活跃商品(WHERE条件)
sql复制SELECT product_name, price
FROM products
WHERE is_active = TRUE;
- 查询价格区间商品(BETWEEN)
sql复制SELECT product_name, price
FROM products
WHERE price BETWEEN 50 AND 200;
- 查询特定分类商品(IN操作符)
sql复制SELECT product_name, category_id
FROM products
WHERE category_id IN (1,3,5);
- 模糊查询商品名(LIKE)
sql复制SELECT product_name
FROM products
WHERE product_name LIKE '%手机%';
- 查询结果排序(ORDER BY)
sql复制SELECT product_name, price
FROM products
ORDER BY price DESC, product_name ASC;
- 分页查询(LIMIT)
sql复制SELECT product_name, price
FROM products
ORDER BY price DESC
LIMIT 10 OFFSET 20; -- 第三页,每页10条
- 去重查询(DISTINCT)
sql复制SELECT DISTINCT category_id
FROM products;
- 列别名使用(AS)
sql复制SELECT
product_name AS '商品名称',
price AS '价格'
FROM products;
- 条件组合查询(AND/OR)
sql复制SELECT product_name, price, stock
FROM products
WHERE (price > 100 OR stock < 50)
AND is_active = TRUE;
3.2 聚合函数深度练习
- 统计商品总数(COUNT)
sql复制SELECT COUNT(*) AS total_products
FROM products;
- 计算平均价格(AVG)
sql复制SELECT AVG(price) AS avg_price
FROM products
WHERE is_active = TRUE;
- 最高/最低价商品(MAX/MIN)
sql复制SELECT
MAX(price) AS max_price,
MIN(price) AS min_price
FROM products;
- 库存总量(SUM)
sql复制SELECT SUM(stock) AS total_stock
FROM products;
- 分组统计(GROUP BY)
sql复制SELECT
category_id,
COUNT(*) AS product_count,
AVG(price) AS avg_price
FROM products
GROUP BY category_id;
- 分组后过滤(HAVING)
sql复制SELECT
category_id,
COUNT(*) AS product_count
FROM products
GROUP BY category_id
HAVING COUNT(*) > 5;
- 多列分组
sql复制SELECT
category_id,
is_active,
COUNT(*) AS count
FROM products
GROUP BY category_id, is_active;
- 分组后排序
sql复制SELECT
category_id,
AVG(price) AS avg_price
FROM products
GROUP BY category_id
ORDER BY avg_price DESC;
- 条件计数(COUNT+IF)
sql复制SELECT
COUNT(*) AS total,
COUNT(IF(price>100,1,NULL)) AS high_price_count
FROM products;
- 分组拼接字符串(GROUP_CONCAT)
sql复制SELECT
category_id,
GROUP_CONCAT(product_name SEPARATOR '|') AS products
FROM products
GROUP BY category_id;
4. 多表关联查询实战
4.1 连接查询基础
- 内连接查询(INNER JOIN)
sql复制SELECT
p.product_name,
c.category_name
FROM products p
INNER JOIN categories c ON p.category_id = c.category_id;
- 左外连接(LEFT JOIN)
sql复制SELECT
u.username,
o.order_id
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id;
- 右外连接(RIGHT JOIN)
sql复制SELECT
o.order_id,
a.province
FROM orders o
RIGHT JOIN user_addresses a ON o.user_id = a.user_id;
- 全外连接模拟(UNION)
sql复制SELECT u.user_id, o.order_id
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
UNION
SELECT u.user_id, o.order_id
FROM users u
RIGHT JOIN orders o ON u.user_id = o.user_id
WHERE u.user_id IS NULL;
- 自连接查询(同一表连接)
sql复制SELECT
c1.category_name,
c2.category_name AS parent_category
FROM categories c1
LEFT JOIN categories c2 ON c1.parent_id = c2.category_id;
4.2 复杂关联查询
- 多表连接
sql复制SELECT
u.username,
o.order_id,
p.product_name,
oi.quantity
FROM users u
JOIN orders o ON u.user_id = o.user_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id;
- 连接过滤条件
sql复制SELECT
p.product_name,
oi.quantity,
o.order_date
FROM products p
JOIN order_items oi ON p.product_id = oi.product_id
JOIN orders o ON oi.order_id = o.order_id
WHERE o.order_date > '2023-01-01'
AND p.category_id = 3;
- 连接+聚合组合
sql复制SELECT
c.category_name,
COUNT(oi.item_id) AS sales_count,
SUM(oi.quantity * oi.unit_price) AS total_sales
FROM categories c
LEFT JOIN products p ON c.category_id = p.category_id
LEFT JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY c.category_id;
- 不等值连接
sql复制SELECT
p1.product_name AS product1,
p2.product_name AS product2
FROM products p1
JOIN products p2 ON p1.price > p2.price * 2;
- 自然连接(不推荐生产环境使用)
sql复制SELECT
u.username,
o.order_id
FROM users u
NATURAL JOIN orders o;
5. 子查询与高级特性
5.1 子查询应用
- WHERE子查询
sql复制SELECT product_name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);
- IN子查询
sql复制SELECT username
FROM users
WHERE user_id IN (
SELECT DISTINCT user_id
FROM orders
WHERE total_amount > 1000
);
- EXISTS子查询
sql复制SELECT c.category_name
FROM categories c
WHERE EXISTS (
SELECT 1
FROM products p
WHERE p.category_id = c.category_id
AND p.stock < 10
);
- FROM子句子查询
sql复制SELECT
avg_stats.category_id,
avg_stats.avg_price
FROM (
SELECT
category_id,
AVG(price) AS avg_price
FROM products
GROUP BY category_id
) AS avg_stats
WHERE avg_stats.avg_price > 150;
- 关联子查询
sql复制SELECT
p1.product_name,
p1.price
FROM products p1
WHERE p1.price > (
SELECT AVG(p2.price)
FROM products p2
WHERE p2.category_id = p1.category_id
);
5.2 高级查询技术
- 公用表表达式(CTE)
sql复制WITH high_value_orders AS (
SELECT order_id, total_amount
FROM orders
WHERE total_amount > 500
)
SELECT
u.username,
hvo.total_amount
FROM users u
JOIN high_value_orders hvo ON u.user_id = hvo.user_id;
- 递归CTE(查询分类层级)
sql复制WITH RECURSIVE category_tree AS (
-- 基础查询:顶级分类
SELECT category_id, category_name, parent_id, 1 AS level
FROM categories
WHERE parent_id IS NULL
UNION ALL
-- 递归查询:子分类
SELECT c.category_id, c.category_name, c.parent_id, ct.level + 1
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.category_id
)
SELECT * FROM category_tree
ORDER BY level, category_id;
- 窗口函数(ROW_NUMBER)
sql复制SELECT
product_name,
price,
ROW_NUMBER() OVER (ORDER BY price DESC) AS price_rank
FROM products;
- 窗口函数(PARTITION BY)
sql复制SELECT
category_id,
product_name,
price,
RANK() OVER (PARTITION BY category_id ORDER BY price DESC) AS price_rank_in_category
FROM products;
- 累计计算(SUM OVER)
sql复制SELECT
order_date,
total_amount,
SUM(total_amount) OVER (ORDER BY order_date) AS running_total
FROM orders;
6. 数据修改与事务控制
6.1 数据操作语言(DML)
- 插入单条记录
sql复制INSERT INTO products (product_name, category_id, price, stock)
VALUES ('无线蓝牙耳机', 5, 299.00, 100);
- 批量插入数据
sql复制INSERT INTO products (product_name, category_id, price, stock)
VALUES
('智能手表', 3, 899.00, 50),
('平板电脑', 3, 1999.00, 30),
('机械键盘', 4, 399.00, 80);
- 插入查询结果
sql复制INSERT INTO product_archive (product_id, product_name, price)
SELECT product_id, product_name, price
FROM products
WHERE is_active = FALSE;
- 更新单列数据
sql复制UPDATE products
SET price = price * 0.9 -- 打9折
WHERE category_id = 3;
- 条件更新多列
sql复制UPDATE users
SET
vip_level = '黄金',
email = CONCAT('gold_', email)
WHERE user_id IN (
SELECT user_id
FROM orders
GROUP BY user_id
HAVING SUM(total_amount) > 5000
);
- 删除特定记录
sql复制DELETE FROM products
WHERE stock = 0
AND is_active = FALSE;
- 清空表数据
sql复制TRUNCATE TABLE temp_orders;
6.2 事务控制实战
- 基本事务流程
sql复制START TRANSACTION;
UPDATE accounts SET balance = balance - 500 WHERE user_id = 1001;
UPDATE accounts SET balance = balance + 500 WHERE user_id = 1002;
-- 检查是否有错误
IF @@ERROR_COUNT = 0 THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
- 保存点使用
sql复制START TRANSACTION;
INSERT INTO orders (user_id, total_amount) VALUES (101, 1200);
SAVEPOINT order_created;
UPDATE products SET stock = stock - 1 WHERE product_id = 5;
-- 假设这里库存不足导致失败
ROLLBACK TO SAVEPOINT order_created;
-- 可以尝试其他操作或取消订单
COMMIT;
- 事务隔离级别设置
sql复制SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
START TRANSACTION;
-- 执行查询
COMMIT;
7. 视图与存储过程
7.1 视图创建与应用
- 创建简单视图
sql复制CREATE VIEW active_products_view AS
SELECT product_id, product_name, price
FROM products
WHERE is_active = TRUE;
- 使用视图查询
sql复制SELECT * FROM active_products_view
WHERE price < 100;
- 创建连接视图
sql复制CREATE VIEW order_detail_view AS
SELECT
o.order_id,
u.username,
o.order_date,
oi.product_id,
p.product_name,
oi.quantity,
oi.unit_price
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id;
- 视图更新限制
sql复制-- 可更新视图示例
CREATE VIEW updatable_products AS
SELECT product_id, product_name, price
FROM products
WHERE category_id = 1
WITH CHECK OPTION;
-- 此更新会成功
UPDATE updatable_products SET price = 99 WHERE product_id = 101;
-- 此更新会失败(违反CHECK OPTION)
UPDATE updatable_products SET category_id = 2 WHERE product_id = 101;
- 修改和删除视图
sql复制ALTER VIEW active_products_view AS
SELECT product_id, product_name, price, stock
FROM products
WHERE is_active = TRUE AND stock > 0;
DROP VIEW IF EXISTS old_product_view;
7.2 存储过程开发
- 创建基础存储过程
sql复制DELIMITER //
CREATE PROCEDURE get_user_orders(IN user_id INT)
BEGIN
SELECT * FROM orders WHERE user_id = user_id;
END //
DELIMITER ;
-- 调用
CALL get_user_orders(101);
- 带输出参数的存储过程
sql复制DELIMITER //
CREATE PROCEDURE get_order_stats(
IN user_id INT,
OUT total_orders INT,
OUT total_amount DECIMAL(12,2)
)
BEGIN
SELECT COUNT(*), SUM(total_amount)
INTO total_orders, total_amount
FROM orders
WHERE user_id = user_id;
END //
DELIMITER ;
-- 调用
CALL get_order_stats(101, @order_count, @order_total);
SELECT @order_count, @order_total;
- 条件逻辑存储过程
sql复制DELIMITER //
CREATE PROCEDURE update_product_price(
IN product_id INT,
IN increase_percent DECIMAL(5,2)
)
BEGIN
DECLARE current_price DECIMAL(10,2);
SELECT price INTO current_price FROM products WHERE product_id = product_id;
IF current_price * (1 + increase_percent/100) > 1000 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Price cannot exceed 1000 after increase';
ELSE
UPDATE products
SET price = price * (1 + increase_percent/100)
WHERE product_id = product_id;
END IF;
END //
DELIMITER ;
- 循环处理存储过程
sql复制DELIMITER //
CREATE PROCEDURE apply_discount_to_category(
IN category_id INT,
IN discount_percent DECIMAL(5,2)
)
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE p_id INT;
DECLARE cur CURSOR FOR
SELECT product_id FROM products WHERE category_id = category_id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur;
read_loop: LOOP
FETCH cur INTO p_id;
IF done THEN
LEAVE read_loop;
END IF;
UPDATE products
SET price = price * (1 - discount_percent/100)
WHERE product_id = p_id;
END LOOP;
CLOSE cur;
END //
DELIMITER ;
- 错误处理存储过程
sql复制DELIMITER //
CREATE PROCEDURE safe_delete_product(
IN product_id INT
)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SELECT 'Error occurred - transaction rolled back' AS message;
END;
START TRANSACTION;
-- 检查是否有订单关联
IF EXISTS (SELECT 1 FROM order_items WHERE product_id = product_id) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot delete product with existing orders';
END IF;
-- 删除操作
DELETE FROM products WHERE product_id = product_id;
COMMIT;
SELECT 'Product deleted successfully' AS message;
END //
DELIMITER ;
8. 索引与性能优化
8.1 索引创建与使用
- 创建单列索引
sql复制CREATE INDEX idx_product_name ON products(product_name);
- 创建复合索引
sql复制CREATE INDEX idx_category_price ON products(category_id, price);
- 唯一索引
sql复制CREATE UNIQUE INDEX idx_unique_email ON users(email);
- 查看表索引
sql复制SHOW INDEX FROM products;
- 删除索引
sql复制DROP INDEX idx_product_name ON products;
8.2 查询性能分析
- 使用EXPLAIN分析查询
sql复制EXPLAIN SELECT * FROM orders WHERE user_id = 101;
- 分析索引使用情况
sql复制EXPLAIN FORMAT=JSON
SELECT p.product_name, COUNT(oi.item_id)
FROM products p
LEFT JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY p.product_id;
- 识别全表扫描
sql复制-- 检查Extra列中的"Using where; Using filesort"等提示
EXPLAIN SELECT * FROM products WHERE price > 100 ORDER BY product_name;
- 强制使用索引
sql复制SELECT * FROM products FORCE INDEX (idx_category_price)
WHERE category_id = 3 AND price > 100;
- 忽略索引
sql复制SELECT * FROM products IGNORE INDEX (idx_category_price)
WHERE category_id = 3;
9. 高级特性与技巧
9.1 JSON数据处理
- 创建JSON列
sql复制ALTER TABLE products ADD COLUMN specs JSON;
- 插入JSON数据
sql复制UPDATE products
SET specs = '{
"color": "black",
"weight": "350g",
"dimensions": {
"length": 15,
"width": 8,
"height": 1
}
}'
WHERE product_id = 101;
- 查询JSON字段
sql复制SELECT
product_name,
specs->>"$.color" AS color,
specs->>"$.dimensions.length" AS length
FROM products;
- JSON路径查询
sql复制SELECT
product_name,
JSON_EXTRACT(specs, '$.dimensions.width') AS width
FROM products
WHERE specs->>"$.color" = 'black';
- 修改JSON数据
sql复制UPDATE products
SET specs = JSON_SET(specs, '$.color', 'blue')
WHERE product_id = 101;
9.2 正则表达式应用
- 正则匹配查询
sql复制SELECT username FROM users
WHERE username REGEXP '^[a-z]{2}[0-9]{4}$';
- 正则替换
sql复制UPDATE users
SET email = REGEXP_REPLACE(email, '@olddomain.com$', '@newdomain.com')
WHERE email REGEXP '@olddomain.com$';
- 提取字符串部分
sql复制SELECT
product_name,
REGEXP_SUBSTR(product_name, '[A-Z]{2,3}[0-9]{4}') AS model_number
FROM products
WHERE product_name REGEXP '[A-Z]{2,3}[0-9]{4}';
- 复杂正则验证
sql复制SELECT email FROM users
WHERE email REGEXP '^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}$';
- 正则分组提取
sql复制SELECT
product_name,
REGEXP_REPLACE(
product_name,
'([0-9]{4})([A-Z]{2})',
'\\1-\\2'
) AS formatted_code
FROM products
WHERE product_name REGEXP '[0-9]{4}[A-Z]{2}';
10. 实战综合练习
10.1 电商业务场景
- 查询每个用户的订单总数和总金额
sql复制SELECT
u.user_id,
u.username,
COUNT(o.order_id) AS order_count,
SUM(o.total_amount) AS total_spent
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id;
- 查询热销商品TOP10
sql复制SELECT
p.product_id,
p.product_name,
SUM(oi.quantity) AS total_sold
FROM products p
JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY p.product_id
ORDER BY total_sold DESC
LIMIT 10;
- 查询有购买但最近30天未登录的用户
sql复制SELECT DISTINCT u.user_id, u.username
FROM users u
JOIN orders o ON u.user_id = o.user_id
WHERE u.last_login < DATE_SUB(NOW(), INTERVAL 30 DAY)
AND o.order_date > DATE_SUB(NOW(), INTERVAL 30 DAY);
- 查询每个分类的销售额占比
sql复制SELECT
c.category_name,
SUM(oi.quantity * oi.unit_price) AS category_sales,
SUM(oi.quantity * oi.unit_price) /
(SELECT SUM(quantity * unit_price) FROM order_items) * 100 AS percentage
FROM categories c
JOIN products p ON c.category_id = p.category_id
JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY c.category_id
ORDER BY category_sales DESC;
- 查询有跨分类购买行为的用户
sql复制SELECT DISTINCT u.user_id, u.username
FROM users u
WHERE (
SELECT COUNT(DISTINCT p.category_id)
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
WHERE o.user_id = u.user_id
) > 1;
10.2 复杂分析场景
- 查询连续3天有登录的用户
sql复制WITH user_login_dates AS (
SELECT
user_id,
DATE(login_time) AS login_date,
LAG(DATE(login_time), 2) OVER (PARTITION BY user_id ORDER BY login_time) AS prev_prev_date
FROM user_logins
)
SELECT DISTINCT user_id
FROM user_login_dates
WHERE DATEDIFF(login_date, prev_prev_date) = 2;
- 计算用户购买频率
sql复制SELECT
user_id,
COUNT(*) AS total_orders,
DATEDIFF(MAX(order_date), MIN(order_date)) AS days_active,
COUNT(*) / NULLIF(DATEDIFF(MAX(order_date), MIN(order_date)), 0) * 30 AS orders_per_month
FROM orders
GROUP BY user_id
HAVING COUNT(*) > 1;
- 查询有升级潜力的用户(差X元升级)
sql复制SELECT
u.user_id,
u.username,
u.vip_level,
SUM(o.total_amount) AS total_spent,
CASE u.vip_level
WHEN '普通' THEN 1000 - SUM(o.total_amount)
WHEN '白银' THEN 3000 - SUM(o.total_amount)
WHEN '黄金' THEN 8000 - SUM(o.total_amount)
ELSE NULL
END AS needed_for_next_level
FROM users u
JOIN orders o ON u.user_id = o.user_id
WHERE u.vip_level IN ('普通','白银','黄金')
GROUP BY u.user_id
HAVING needed_for_next_level > 0
ORDER BY needed_for_next_level;
- 使用交叉表查询每月销售统计
sql复制SELECT
YEAR(order_date) AS year,
SUM(IF(MONTH(order_date)=1, total_amount, 0)) AS Jan,
SUM(IF(MONTH(order_date)=2, total_amount, 0)) AS Feb,
SUM(IF(MONTH(order_date)=3, total_amount, 0)) AS Mar,
-- 其他月份类似
SUM(total_amount) AS yearly_total
FROM orders
GROUP BY YEAR(order_date)
ORDER BY year;
- 查询有相似购买行为的用户对
sql复制SELECT
u1.user_id AS user1,
u2.user_id AS user2,
COUNT(DISTINCT oi1.product_id) AS common_products
FROM users u1
JOIN orders o1 ON u1.user_id = o1.user_id
JOIN order_items oi1 ON o1.order_id = oi1.order_id
JOIN order_items oi2 ON oi1.product_id = oi2.product_id
JOIN orders o2 ON oi2.order_id = o2.order_id
JOIN users u2 ON o2.user_id = u2.user_id
WHERE u1.user_id < u2.user_id
GROUP BY u1.user_id, u2.user_id
HAVING COUNT(DISTINCT oi1.product_id) > 3
ORDER BY common_products DESC;
11. 性能优化实战
11.1 慢查询优化案例
- 优化分组统计查询
sql复制-- 原始慢查询
SELECT
c.category_name,
COUNT(oi.item_id) AS sales_count
FROM categories c
LEFT JOIN products p ON c.category_id = p.category_id
LEFT JOIN order_items oi ON p.product_id = oi.product_id
GROUP BY c.category_id;
-- 优化方案1:添加索引
ALTER TABLE order_items ADD INDEX idx_product_id (product_id);
-- 优化方案2:使用子查询减少连接
SELECT
c.category_name,
(SELECT COUNT(*)
FROM order_items oi
JOIN products p ON oi.product_id = p.product_id
WHERE p.category_id = c.category_id) AS sales_count
FROM categories c;
- 优化分页查询
sql复制-- 原始慢分页
SELECT * FROM orders
ORDER BY order_date DESC
LIMIT 10000, 20;
-- 优化方案1:使用索引覆盖
SELECT o.* FROM orders o
JOIN (
SELECT order_id FROM orders
ORDER BY order_date DESC
LIMIT 10000, 20
) AS tmp ON o.order_id = tmp.order_id;
-- 优化方案2:使用游标分页
SELECT * FROM orders
WHERE order_date < '2023-06-01'
ORDER BY order_date DESC
LIMIT 20;
11.2 执行计划分析
- 分析复杂连接查询
sql复制EXPLAIN
SELECT
u.username,
p.product_name,
COUNT(oi.item_id) AS purchase_count
FROM users u
JOIN orders o ON u.user_id = o.user_id
JOIN order_items oi ON o.order_id = oi.order_id
JOIN products p ON oi.product_id = p.product_id
GROUP BY u.user_id, p.product_id;
- 识别全表扫描
sql复制-- 检查type列是否为ALL
EXPLAIN SELECT * FROM products WHERE product_name LIKE '%Pro%';
- 优化排序操作
sql复制-- 检查Extra列是否有"Using filesort"
EXPLAIN SELECT * FROM products ORDER BY category_id, price DESC;
-- 添加复合索引
ALTER TABLE products ADD INDEX idx_category_price (category_id, price DESC);
12. 安全与维护
12.1 数据备份与恢复
- 使用mysqldump备份
bash复制mysqldump -u username -p sql_practice > sql_practice_backup.sql
- 恢复数据库
bash复制mysql -u username -p sql_practice < sql_practice_backup.sql
12.2 用户权限管理
- 创建只读用户
sql复制CREATE USER 'report_user'@'%' IDENTIFIED BY 'ReadOnly@123';
GRANT SELECT ON sql_practice.* TO 'report_user'@'%';
- 权限回收
sql复制REVOKE ALL PRIVILEGES ON *.* FROM 'old_user'@'localhost';
- 查看用户权限
sql复制SHOW GRANTS FOR 'current_user'@'localhost';
13. 持续学习建议
完成这100道基础练习题后,建议从以下几个方向继续深入学习:
- 研究MySQL官方文档中的高级特性,如窗口函数、CTE、JSON处理等
- 学习查询优化技巧,掌握EXPLAIN执行计划分析
- 实践复杂业务场景的SQL实现,如漏斗分析、留存计算等
- 了解MySQL的架构原理,包括存储引擎、索引实现、事务隔离等
- 探索其他数据库系统如PostgreSQL的特性对比
我在实际工作中发现,SQL能力的提升关键在于持续实践和反思。建议定期回顾这些基础题目,随着经验增长,你会对相同的题目有更深层次的理解。当你能轻松解决90%以上题目时,就可以开始挑战更复杂的业务分析场景了。
