7人参与 • 2025-06-09 • Mysql
mysql 中复杂 sql 的核心部分:多表联查和子查询。这是数据库操作中处理关联数据的强大工具。
核心目标: 从多个相互关联的表中组合和提取所需的数据。
当你的数据模型设计良好(遵循规范化原则)时,数据会分散在多个表中,通过主键-外键关系连接。join 操作就是用来基于这些关系将多个表中的行组合起来。
select 列名列表 from 表1 [inner] join 表2 on 表1.关联字段 = 表2.关联字段 [where 条件]; -- inner 关键字通常可省略
示例: 查询所有有订单的客户信息(假设 customers
表有 customer_id
,orders
表有 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表中至少有一个订单的客户。
图示: 两个集合的交集部分。
null
。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。
图示: 整个左集合 + 与右集合的交集部分。右集合独有的部分被舍弃。
left join
相反。返回右表 (表2) 的所有行,即使在左表 (表1) 中没有匹配的行。对于右表中存在而左表中没有匹配的行,左表相关的列将显示为 null
。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。
right join
在实际应用中不如 left join
常见,因为通常可以通过调整表顺序使用 left join
达到相同目的。null
。如果两个表中有匹配的行,则进行连接。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没有的行)
表1行数 * 表2行数
。通常不是你想要的结果,除非明确需要所有组合。select 列名列表 from 表1 cross join 表2; -- 或者使用隐式连接(不推荐): select 列名列表 from 表1, 表2;
示例: 生成所有产品和所有尺寸的组合
select p.product_name, s.size_name from products p cross join sizes s;
join
子句连接多个表。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;
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)
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
子查询是指嵌套在另一个 sql 查询(主查询)内部的查询。子查询的结果被外部查询使用。
select
子句(标量子查询)from
子句(派生表/内联视图)where
子句(最常用)having
子句insert
/ update
/ delete
语句的 values
或 set
部分select
列表、where
条件中的比较运算符右侧)。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;
in
, any
/some
, all
运算符一起用在 where
或 having
子句中。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' );
> any
等价于 > (select min(price) from ... where category='electronics')
)select product_name, price from products where category <> 'electronics' and price > any ( select price from products where category = 'electronics' );
> all
等价于 > (select max(price) from ... where category='electronics')
)select product_name, price from products where category <> 'electronics' and price > all ( select price from products where category = 'electronics' );
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; -- 排除自己
from
子句中,并且必须有别名。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;
非相关子查询 (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
例子)
专门用于相关子查询(但也可以用于非相关)。
exists (subquery)
: 如果子查询返回至少一行,则结果为 true
。not exists (subquery)
: 如果子查询返回零行,则结果为 true
。exists
就立即返回 true
,不需要处理所有结果。in
示例,但可能更高效)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多表联查/子查询内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论