Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > mysql多表联查子查询

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

作者:岫珩

掌握多表联查 (INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN) 和子查询(标量、列、行、表子查询、相关/非相关、EXISTS/NOT EXISTS)是进行复杂数据库查询的基础,这篇文章主要介绍了MySQL复杂SQL(多表联查/子查询)详细讲解,需要的朋友可以参考下

MySQL 中复杂 SQL 的核心部分:多表联查子查询。这是数据库操作中处理关联数据的强大工具。

核心目标: 从多个相互关联的表中组合和提取所需的数据。

第一部分:多表联查 (JOIN Operations)

当你的数据模型设计良好(遵循规范化原则)时,数据会分散在多个表中,通过主键-外键关系连接。JOIN 操作就是用来基于这些关系将多个表中的行组合起来。

1. 连接的类型 (JOIN Types)

a. INNER JOIN (内连接 / 等值连接)

SELECT 列名列表
FROM 表1
[INNER] JOIN 表2 ON 表1.关联字段 = 表2.关联字段
[WHERE 条件];
-- INNER 关键字通常可省略

示例: 查询所有有订单的客户信息(假设 customers 表有 customer_idorders 表有 customer_id 外键)

SELECT c.customer_id, c.name, o.order_id, o.order_date
FROM customers c
INNER JOIN orders o ON c.customer_id = o.customer_id;
-- 结果只包含那些在customers表中有记录且在orders表中至少有一个订单的客户。

图示: 两个集合的交集部分。

b. LEFT [OUTER] JOIN (左外连接)

SELECT 列名列表
FROM 表1
LEFT [OUTER] JOIN 表2 ON 表1.关联字段 = 表2.关联字段
[WHERE 条件];
-- OUTER 关键字通常可省略

示例: 查询所有客户及其订单(包括没有下过单的客户)

SELECT c.customer_id, c.name, o.order_id, o.order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id;
-- 结果包含所有客户。对于没有订单的客户,o.order_id 和 o.order_date 会是 NULL。

图示: 整个左集合 + 与右集合的交集部分。右集合独有的部分被舍弃。

c. RIGHT [OUTER] JOIN (右外连接)

SELECT 列名列表
FROM 表1
RIGHT [OUTER] JOIN 表2 ON 表1.关联字段 = 表2.关联字段
[WHERE 条件];
-- OUTER 关键字通常可省略
SELECT c.customer_id, c.name, o.order_id, o.order_date
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id;
-- 结果包含所有订单。如果某个订单的 customer_id 在 customers 表中找不到,则 c.customer_id 和 c.name 会是 NULL。

d. FULL [OUTER] JOIN (全外连接)

SELECT 列名列表
FROM 表1
LEFT JOIN 表2 ON 表1.关联字段 = 表2.关联字段
UNION [ALL] -- 通常用 UNION 去重,如果确定不会有重复或需要保留重复则用 UNION ALL
SELECT 列名列表
FROM 表1
RIGHT JOIN 表2 ON 表1.关联字段 = 表2.关联字段
WHERE 表1.关联字段 IS NULL; -- 排除掉左连接中已包含的匹配行

示例: 查询所有客户和所有订单(包括没有订单的客户和没有对应客户的订单)

SELECT c.customer_id, c.name, o.order_id, o.order_date
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
UNION
SELECT c.customer_id, c.name, o.order_id, o.order_date
FROM customers c
RIGHT JOIN orders o ON c.customer_id = o.customer_id
WHERE c.customer_id IS NULL; -- 只取右连接中左表为NULL的部分(即orders有而customers没有的行)

e. CROSS JOIN (交叉连接 / 笛卡尔积)

SELECT 列名列表
FROM 表1
CROSS JOIN 表2;
-- 或者使用隐式连接(不推荐):
SELECT 列名列表
FROM 表1, 表2;

示例: 生成所有产品和所有尺寸的组合

SELECT p.product_name, s.size_name
FROM products p
CROSS JOIN sizes s;

2. 多表连接 (Joining More Than Two Tables)

SELECT ...
FROM 表1
JOIN 表2 ON 条件
JOIN 表3 ON 条件 -- 条件可以是表2和表3的关系,或者表1和表3的关系(较少见)
...
[WHERE ...];

示例: 查询订单的详细信息(客户名、订单日期、产品名、数量)

SELECT c.name, o.order_date, p.product_name, od.quantity
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
JOIN order_details od ON o.order_id = od.order_id
JOIN products p ON od.product_id = p.product_id;

3. 自连接 (Self Join)

SELECT e1.employee_name AS Employee, e2.employee_name AS Manager
FROM employees e1
LEFT JOIN employees e2 ON e1.manager_id = e2.employee_id;
-- 使用 LEFT JOIN 是因为顶级经理没有上级(manager_id 为 NULL)

4. 自然连接 (NATURAL JOIN) 和 USING 子句

SELECT ... FROM table1 NATURAL JOIN table2; -- 避免使用

USING 子句: 当连接的两个表具有完全相同名称的关联字段时,可以用 USING 简化 ON

SELECT c.customer_id, c.name, o.order_id, o.order_date
FROM customers c
JOIN orders o USING (customer_id); -- 等价于 ON c.customer_id = o.customer_id

第二部分:子查询 (Subqueries)

子查询是指嵌套在另一个 SQL 查询(主查询)内部的查询。子查询的结果被外部查询使用。

1. 子查询的位置 (Where Subqueries Can Be Used)

2. 子查询的主要类型

a. 标量子查询 (Scalar Subquery)

SELECT product_name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products);

示例:SELECT 列表中使用(为每行计算一个相关值)

SELECT order_id, order_date,
       (SELECT COUNT(*) FROM order_details od WHERE od.order_id = o.order_id) AS item_count
FROM orders o;

b. 列子查询 (Column Subquery)

SELECT customer_id, name
FROM customers
WHERE customer_id IN (
    SELECT DISTINCT o.customer_id
    FROM orders o
    JOIN order_details od ON o.order_id = od.order_id
    JOIN products p ON od.product_id = p.product_id
    WHERE p.product_name = 'Coffee'
);
SELECT product_name, price
FROM products
WHERE category <> 'Electronics'
  AND price > ANY (
      SELECT price
      FROM products
      WHERE category = 'Electronics'
  );
SELECT product_name, price
FROM products
WHERE category <> 'Electronics'
  AND price > ALL (
      SELECT price
      FROM products
      WHERE category = 'Electronics'
  );

c. 行子查询 (Row Subquery)

SELECT employee_id, name, department, job_level
FROM employees
WHERE (department, job_level) = (
    SELECT department, job_level
    FROM employees
    WHERE employee_id = 123
)
AND employee_id <> 123; -- 排除自己

d. 表子查询 / 派生表 (Table Subquery / Derived Table)

SELECT p.product_id, p.product_name, p.category, p.price, cat_avg.avg_price
FROM products p
JOIN (
    SELECT category, AVG(price) AS avg_price
    FROM products
    GROUP BY category
) cat_avg ON p.category = cat_avg.category
WHERE p.price > cat_avg.avg_price;

3. 相关子查询 vs. 非相关子查询

非相关子查询 (Uncorrelated Subquery):

相关子查询 (Correlated Subquery):

SELECT c.customer_id, c.name
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id -- 关联条件
    GROUP BY o.customer_id
    HAVING SUM(o.total_amount) > 1000
);
-- 或者更高效的方式可能是使用 JOIN + GROUP BY + HAVING

示例:SELECT 列表中使用相关子查询 (如之前的 item_count 例子)

4. EXISTS 和 NOT EXISTS

专门用于相关子查询(但也可以用于非相关)。

SELECT customer_id, name
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id -- 关联条件
);

示例 (NOT EXISTS): 查询从未下过订单的客户

SELECT customer_id, name
FROM customers c
WHERE NOT EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id -- 关联条件
);

关键注意事项与最佳实践

总结

掌握多表联查 (INNER JOIN, LEFT JOIN, RIGHT JOIN, FULL JOIN) 和子查询(标量、列、行、表子查询、相关/非相关、EXISTS/NOT EXISTS)是进行复杂数据库查询的基础。理解它们的工作原理、适用场景以及性能影响至关重要。通过实践、关注索引、编写清晰的 SQL 并利用 EXPLAIN 分析,你将能够高效地从关联的数据库表中提取所需的信息。记住,清晰性和性能往往是相辅相成的。

到此这篇关于MySQL复杂SQL(多表联查/子查询)详细讲解的文章就介绍到这了,更多相关mysql多表联查/子查询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文