MySQL子查询零基础入门教程:从小白到上手(零基础入门版)

📚 前言:什么是子查询?

想象一下,你想在图书馆找书:

先找到"计算机"分类的书架(主查询)

在这个书架上找"MySQL"相关的书(子查询)

子查询就是:在一个SQL语句里面,嵌套另一个SQL查询语句。就像"俄罗斯套娃"一样,一层套一层!

第一部分:子查询的三种基础用法

1.1 最简单的子查询:作为查询条件

场景:找出买了"iPhone 15"的顾客信息

bash 复制代码
sql
-- 分两步理解:
-- 第一步:先找出哪些订单买了iPhone 15
SELECT customer_id 
FROM orders 
WHERE product_name = 'iPhone 15';

-- 第二步:用第一步的结果找顾客信息
SELECT * 
FROM customers 
WHERE customer_id IN (上一步的结果);

-- 合并成一句就是:
SELECT * 
FROM customers 
WHERE customer_id IN (
    SELECT customer_id 
    FROM orders 
    WHERE product_name = 'iPhone 15'
);

📝 语法要点:

子查询要用小括号 () 包起来

子查询写在 WHERE 条件里

IN 表示"在...列表中"

1.2 稍微复杂点:子查询在SELECT中

场景:显示每个顾客的订单数量

bash 复制代码
sql
-- 传统方法(需要两张表连接):
SELECT 
    c.customer_name,
    COUNT(o.order_id) as order_count
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
GROUP BY c.customer_name;

-- 使用子查询的方法:
SELECT 
    customer_name,
    (SELECT COUNT(*) 
     FROM orders 
     WHERE customer_id = c.customer_id) as order_count
FROM customers c;

✨ 对比:

子查询版本更直观:一眼就能看出是"为每个顾客统计订单数"

适合初学者理解

1.3 实战练习:生活中的例子

bash 复制代码
sql
-- 找出比平均工资高的员工
SELECT name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary)  -- 先算平均工资
    FROM employees
);

-- 找出最畅销的产品
SELECT product_name
FROM products
WHERE sales = (
    SELECT MAX(sales)  -- 先找最高销量
    FROM products
);

第二部分:用子查询操作数据(增删改)

2.1 用子查询插入数据 📥

场景:创建"VIP客户表",把消费超过5000的客户放进去

bash 复制代码
sql
-- 第一步:创建VIP表
CREATE TABLE vip_customers (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    total_spent DECIMAL(10,2)
);

-- 第二步:插入数据(使用子查询)
INSERT INTO vip_customers (id, name, total_spent)
SELECT 
    customer_id,
    customer_name,
    SUM(amount)  -- 计算每个客户的总消费
FROM orders
GROUP BY customer_id, customer_name
HAVING SUM(amount) > 5000;  -- 只选消费>5000的

💡 小贴士:

INSERT INTO ... SELECT ... 是最常用的组合

子查询的结果直接插入到目标表

字段要一一对应(数量、类型都要匹配)

2.2 用子查询更新数据 ✏️

场景:给消费大户打标签

bash 复制代码
sql
-- 给消费超过10000的客户标记为"钻石会员"
UPDATE customers
SET member_level = '钻石会员'
WHERE customer_id IN (
    SELECT customer_id
    FROM orders
    GROUP BY customer_id
    HAVING SUM(amount) > 10000
);

-- 分步骤理解:
-- 1. 子查询:找出消费>10000的客户ID
-- 2. 主查询:把这些客户的会员级别更新
⚠️ 注意:MySQL中,更新时如果用同一张表做子查询,需要特殊处理:
bash 复制代码
sql
-- 错误写法(MySQL不允许):
UPDATE customers
SET score = score + 10
WHERE customer_id IN (
    SELECT customer_id 
    FROM customers 
    WHERE age > 30
);

-- 正确写法:
UPDATE customers c1
SET score = score + 10
WHERE EXISTS (
    SELECT 1 
    FROM customers c2 
    WHERE c2.customer_id = c1.customer_id 
    AND c2.age > 30
);

2.3 用子查询删除数据 🗑️

场景:删除30天未登录的用户

bash 复制代码
sql
-- 删除30天没登录的用户
DELETE FROM users
WHERE user_id IN (
    SELECT user_id
    FROM login_log
    GROUP BY user_id
    HAVING MAX(login_time) < DATE_SUB(NOW(), INTERVAL 30 DAY)
);

-- 更安全的写法(先查后删):

bash 复制代码
-- 第一步:先看看要删哪些人
SELECT * FROM users
WHERE user_id IN (...上面的查询...);

-- 第二步:确认无误后再删除
🔒 安全提示:

删除前一定要先 SELECT 确认数据

重要数据用软删除(标记为删除,不真删)

做好备份!

第三部分:通用表达式(CTE)------让SQL更清晰

3.1 CTE是什么?为什么需要它?

问题:当子查询嵌套太多时...

bash 复制代码
sql
-- 这种代码很难看懂:
SELECT * FROM (
    SELECT * FROM (
        SELECT * FROM table1 WHERE ...
    ) AS t1 JOIN table2 ON ...
) AS t2 WHERE ...

解决方案:CTE(Common Table Expressions)!

给子查询起个名字

像搭积木一样组合查询

提高可读性

3.2 CTE基础语法

bash 复制代码
sql
WITH 临时表名 AS (
    SELECT ...  -- 你的查询
)
SELECT * FROM 临时表名;

3.3 实战:用CTE重构复杂查询

场景:分析学生成绩

bash 复制代码
sql
-- 不使用CTE(比较乱):
SELECT 
    student_name,
    course_name,
    score,
    (SELECT AVG(score) FROM scores WHERE course_id = s.course_id) as avg_score
FROM scores s
WHERE score > (
    SELECT AVG(score) 
    FROM scores 
    WHERE student_id = s.student_id
);

-- 使用CTE(清晰多了):
WITH 
-- 第一步:计算每门课的平均分
course_avg AS (
    SELECT 
        course_id,
        AVG(score) as avg_score
    FROM scores
    GROUP BY course_id
),

-- 第二步:计算每个学生的平均分
student_avg AS (
    SELECT 
        student_id,
        AVG(score) as avg_score
    FROM scores
    GROUP BY student_id
)

-- 第三步:组合查询
SELECT 
    s.student_name,
    c.course_name,
    sc.score,
    ca.avg_score as course_avg,
    sa.avg_score as student_avg
FROM scores sc
JOIN students s ON sc.student_id = s.student_id
JOIN courses c ON sc.course_id = c.course_id
JOIN course_avg ca ON sc.course_id = ca.course_id
JOIN student_avg sa ON sc.student_id = sa.student_id
WHERE sc.score > sa.avg_score;

3.4 CTE的特别功能:递归查询

神奇的场景:查询公司组织架构

bash 复制代码
sql
-- 创建员工表(有上下级关系)
CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    manager_id INT  -- 上级的ID
);

-- 使用递归CTE查询某个经理的所有下属
WITH RECURSIVE subordinate_tree AS (
    -- 基础情况:先找到经理本人
    SELECT id, name, manager_id, 1 as level
    FROM employees
    WHERE name = '张经理'
    
    UNION ALL
    
    -- 递归情况:找下属的下属...
    SELECT 
        e.id, 
        e.name, 
        e.manager_id,
        st.level + 1
    FROM employees e
    JOIN subordinate_tree st ON e.manager_id = st.id
)
SELECT * FROM subordinate_tree;

递归CTE的组成:

基础查询:起点(比如:经理本人)

递归查询:不断找下一级

停止条件:没有更多下级时自动停止

第四部分:常见错误和解决方法

❌ 错误1:子查询返回多行

bash 复制代码
sql
-- 错误:等号只能比较一个值
SELECT * FROM products
WHERE price = (
    SELECT price FROM products WHERE category = '手机'
);

-- 正确:用IN代替=
SELECT * FROM products
WHERE price IN (
    SELECT price FROM products WHERE category = '手机'
);

❌ 错误2:忽略NULL值

bash 复制代码
sql
-- 可能漏掉数据
SELECT * FROM customers
WHERE customer_id NOT IN (
    SELECT customer_id FROM orders
);

-- 正确处理NULL
SELECT * FROM customers
WHERE customer_id NOT IN (
    SELECT customer_id 
    FROM orders 
    WHERE customer_id IS NOT NULL  -- 排除NULL
);

❌ 错误3:性能问题

bash 复制代码
sql
-- 慢:子查询执行很多次
SELECT * FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o
    WHERE o.customer_id = c.customer_id
    AND o.amount > 1000
);

-- 优化:使用JOIN
SELECT DISTINCT c.*
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.amount > 1000;

💪 实战练习题

bash 复制代码
练习1:电商数据分析

sql
-- 1. 找出从未下单的用户
-- 2. 找出复购率最高的商品
-- 3. 计算每个用户的平均订单金额
练习2:员工管理系统

sql
-- 1. 找出每个部门工资最高的员工
-- 2. 找出有下属的经理
-- 3. 计算部门平均工资
练习3:学校成绩管理

sql
-- 1. 找出每科成绩前3名的学生
-- 2. 计算每个学生的总分和平均分
-- 3. 找出成绩进步最大的学生

📖 学习资源推荐

在线练习平台:

SQLZoo(免费,互动式学习)

LeetCode SQL题库(从易到难)

可视化工具:

MySQL Workbench(官方工具)

DataGrip(功能强大)

记忆口诀:

text

子查询,括号里

先执行,后外批

增删改,要小心

CTE让代码清

🎉 总结:关键要点速记

💡 最后的小建议

从简单开始:先写分步骤查询,再合并成子查询

多实践:在自己的项目中找机会使用

善用注释:复杂查询一定要写注释

测试数据:用小数据测试,确认正确后再用到大表

记住:学习SQL就像学做饭,开始可能手忙脚乱,多做几次就熟练了!遇到问题不要怕,多查资料多实践,你一定能掌握子查询这个强大工具! 🚀

祝你学习愉快,早日成为SQL高手! ✨

相关推荐
码界调试侠5 小时前
MongoDB 常用查询语法
数据库·mongodb
静听山水5 小时前
StarRocks导入数据【Stream Load】
数据库
藦卡机器人5 小时前
国产机械臂做的比较好的品牌有哪些?
大数据·数据库·人工智能
jiunian_cn5 小时前
【Redis】数据库管理操作
数据库·redis·缓存
_Johnny_6 小时前
ETCD 配额/空间告警模拟方案
网络·数据库·etcd
猫头虎6 小时前
基于信创openEuler系统安装部署OpenTeleDB开源数据库的实战教程
数据库·redis·sql·mysql·开源·nosql·database
爬山算法6 小时前
MongoDB(1)什么是MongoDB?
数据库·mongodb
Nandeska6 小时前
17、MySQL InnoDB ReplicaSet
数据库·mysql
AI_56786 小时前
SQL性能优化全景指南:从量子执行计划到自适应索引的终极实践
数据库·人工智能·学习·adb