MySQL子查询

掌握在查询中嵌套其他查询的使用方法

子查询概述

子查询(嵌套查询)是指在一个查询语句中嵌套另一个查询语句。子查询可以出现在SELECT、FROM、WHERE等子句中。

子查询分类

  • 标量子查询 - 返回单个值的子查询
  • 列子查询 - 返回单列多行的子查询
  • 行子查询 - 返回单行多列的子查询
  • 表子查询 - 返回多行多列的子查询
  • 相关子查询 - 依赖于外部查询的子查询
  • 非相关子查询 - 独立于外部查询的子查询

标量子查询

标量子查询返回单个值,可以在任何使用标量值的地方使用。

标量子查询示例

SQL示例
-- 查询工资高于平均工资的员工
SELECT name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary) FROM employees
);

-- 查询最新订单的详细信息
SELECT *
FROM orders
WHERE order_date = (
    SELECT MAX(order_date) FROM orders
);

列子查询

列子查询返回单列多行,通常与IN、ANY、ALL等操作符一起使用。

列子查询示例

SQL示例
-- 查询在销售部工作的员工
SELECT name
FROM employees
WHERE department_id IN (
    SELECT id FROM departments WHERE department_name = '销售部'
);

-- 查询没有订单的客户
SELECT customer_name
FROM customers
WHERE customer_id NOT IN (
    SELECT DISTINCT customer_id FROM orders
);

行子查询

行子查询返回单行多列,通常与行比较操作符一起使用。

行子查询示例

SQL示例
-- 查询与张三在同一个部门且职位相同的员工
SELECT name, department, position
FROM employees
WHERE (department, position) = (
    SELECT department, position 
    FROM employees 
    WHERE name = '张三'
);

表子查询

表子查询返回多行多列,可以作为临时表在FROM子句中使用。

表子查询示例

SQL示例
-- 查询每个部门的平均工资
SELECT department, avg_salary
FROM (
    SELECT department, AVG(salary) AS avg_salary
    FROM employees
    GROUP BY department
) AS dept_avg
WHERE avg_salary > 5000;

-- 查询每个客户的订单数量
SELECT c.customer_name, order_count
FROM customers c
JOIN (
    SELECT customer_id, COUNT(*) AS order_count
    FROM orders
    GROUP BY customer_id
) AS oc ON c.customer_id = oc.customer_id;

相关子查询

相关子查询依赖于外部查询,对外部查询的每一行执行一次。

相关子查询示例

SQL示例
-- 查询工资高于所在部门平均工资的员工
SELECT name, department, salary
FROM employees e1
WHERE salary > (
    SELECT AVG(salary)
    FROM employees e2
    WHERE e2.department = e1.department
);

-- 查询每个客户的最近订单
SELECT customer_id, order_id, order_date
FROM orders o1
WHERE order_date = (
    SELECT MAX(order_date)
    FROM orders o2
    WHERE o2.customer_id = o1.customer_id
);

EXISTS和NOT EXISTS子查询

EXISTS用于检查子查询是否返回结果,NOT EXISTS用于检查子查询是否不返回结果。

EXISTS子查询示例

SQL示例
-- 查询有订单的客户
SELECT customer_name
FROM customers c
WHERE EXISTS (
    SELECT 1 FROM orders o 
    WHERE o.customer_id = c.customer_id
);

-- 查询没有订单的客户
SELECT customer_name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1 FROM orders o 
    WHERE o.customer_id = c.customer_id
);

ANY、SOME和ALL操作符

这些操作符用于将值与子查询返回的一组值进行比较。

ANY/SOME和ALL操作符示例

SQL示例
-- 查询工资高于任何销售部员工的员工
SELECT name, salary
FROM employees
WHERE salary > ANY (
    SELECT salary 
    FROM employees 
    WHERE department = '销售部'
);

-- 查询工资高于所有销售部员工的员工
SELECT name, salary
FROM employees
WHERE salary > ALL (
    SELECT salary 
    FROM employees 
    WHERE department = '销售部'
);
注意:ANY和SOME是同义词,可以互换使用。

子查询性能优化

为了提高子查询的性能,可以考虑以下优化策略:

  • 使用JOIN替代相关子查询
  • 在子查询的WHERE条件列上创建索引
  • 使用EXISTS替代IN,特别是在大表上
  • 避免在子查询中使用SELECT *
  • 考虑使用临时表存储子查询结果

使用JOIN替代子查询示例

SQL示例
-- 使用子查询
SELECT name
FROM employees
WHERE department_id IN (
    SELECT id FROM departments WHERE location = '北京'
);

-- 使用JOIN替代
SELECT e.name
FROM employees e
JOIN departments d ON e.department_id = d.id
WHERE d.location = '北京';

子查询常见错误与注意事项

在使用子查询时,需要注意以下常见错误:

  • 子查询返回多行时使用=操作符
  • 在WHERE子句中使用ORDER BY(除非在子查询中)
  • 忘记为子查询结果指定别名
  • 在子查询中引用外部查询的列但未建立正确关联

错误示例

SQL示例
-- 错误:子查询返回多行,不能使用=操作符
SELECT name
FROM employees
WHERE department_id = (
    SELECT id FROM departments WHERE location = '北京'
);

-- 正确:使用IN操作符
SELECT name
FROM employees
WHERE department_id IN (
    SELECT id FROM departments WHERE location = '北京'
);

子查询在不同SQL语句中的应用

子查询不仅可以在SELECT语句中使用,还可以在INSERT、UPDATE和DELETE语句中使用:

在INSERT语句中使用子查询

SQL示例
-- 将高薪员工信息插入到另一张表
INSERT INTO high_salary_employees (employee_id, name, salary)
SELECT employee_id, name, salary
FROM employees
WHERE salary > (
    SELECT AVG(salary) FROM employees
);

在UPDATE语句中使用子查询

SQL示例
-- 将部门平均工资以下的员工薪资提高10%
UPDATE employees e1
SET salary = salary * 1.1
WHERE salary < (
    SELECT AVG(salary)
    FROM employees e2
    WHERE e2.department = e1.department
);

在DELETE语句中使用子查询

SQL示例
-- 删除没有订单的客户
DELETE FROM customers
WHERE customer_id NOT IN (
    SELECT DISTINCT customer_id FROM orders
);

子查询与连接查询的比较

子查询和连接查询都可以用于从多个表中检索数据,但它们各有优缺点:

特性 子查询 连接查询
可读性 对于复杂逻辑更直观 对于简单关系更直观
性能 有时较慢,特别是相关子查询 通常更快
灵活性 可以用于更复杂的条件 适用于简单的关系
维护性 修改复杂逻辑可能困难 通常更容易维护

下一步学习

掌握了子查询后,您可以继续学习:

  • 连接查询 - 学习如何连接多个表进行查询
  • MySQL函数 - 了解MySQL内置函数的使用
  • 事务 - 学习如何管理数据库事务