

在Java后端开发中,MySQL查询是业务落地的核心环节。连表、子查询、UNION查询几乎无处不在,但多数开发者仅满足"能用",却忽视了性能差异——不同实现方式的性能可能相差一个数量级,甚至引发生产环境慢查询。
本文从语法本质、性能对比、通用优化三个维度,拆解三种查询的核心要点,帮你精准选型、规避性能陷阱。
一、核心认知:三种查询的本质与边界
先明确三者的定义与适用场景,从根源上避免用错方式。
1.连表查询(JOIN)
通过表间关联字段整合多表数据,核心价值是"一次性获取多表数据",减少数据库交互次数,是多表查询的主流方案。
| JOIN类型 | 关联逻辑 | 返回结果 | 是否可能出现NULL | 适用场景 |
| INNERJOIN(内连接) | 只保留两表中匹配关联条件的记录 | 交集数据 | 否 | 需获取两表都存在的关联数据,如"查询已下单的用户信息"(用户表+订单表) |
| LEFTJOIN(左连接) | 保留左表所有记录,右表匹配不到则补NULL | 左表全量+右表交集 | 是(右表字段) | 需保留主表全量数据,如"查询所有用户及对应订单(无订单显示空)" |
| RIGHTJOIN(右连接) | 保留右表所有记录,左表匹配不到则补NULL | 右表全量+左表交集 | 是(左表字段) | 场景同左连接,仅表顺序相反,建议统一用LEFTJOIN(可读性更强) |
| CROSSJOIN(交叉连接) | 无关联条件,两表做笛卡尔积 | 左表行数×右表行数 | 否 | 极少用,仅适用于"全组合场景",需严格控制数据量 |
注意:MySQL不支持原生FULLJOIN,可通过"LEFTJOIN+UNIONALL+RIGHTJOIN"模拟,但性能较差,大数据量下不建议使用。
2.子查询(Subquery)
将一个查询嵌套在另一个查询中,内层结果作为外层的条件或数据源,核心价值是"基于子结果集二次筛选",逻辑更贴近业务思考。
非相关子查询:内层不依赖外层字段,可独立执行,仅运行一次,适合内层结果集较小的场景。
相关子查询:内层依赖外层字段,需逐行关联执行,嵌套循环逻辑,大数据量下性能极差,建议优先改写。
3.UNION查询
合并多个SELECT结果集,要求各语句字段数量、类型、顺序一致,核心价值是"整合同结构分散数据"。
UNION:合并后去重,需额外排序,性能开销大,仅在需去重时使用。
UNIONALL:直接合并不排序、不去重,性能远优于UNION,是优先选择(确保无重复或允许重复时)。
二、性能对比与选型建议
三种查询无绝对优劣,需结合数据量、索引、业务逻辑判断,以下是核心选型指南。
1.连表查询vs子查询
MySQL5.6+已优化部分子查询(转为JOIN执行),但差异仍明显:
非相关子查询:适合内层结果集小的简单筛选,逻辑清晰;若内层结果量大,缓存开销高,性能不及JOIN。
相关子查询:逐行执行效率低,大数据量下必须改写为JOIN。
案例优化:相关子查询改写为JOIN
```sql
优化前:相关子查询(低效)
SELECTu.idFROMuseru
WHEREEXISTS(SELECT1FROMorderoWHEREo.user_id=u.idANDo.amount>1000);
优化后:JOIN查询(高效)
SELECTDISTINCTu.idFROMuseru
INNERJOINorderoONu.id=o.user_id
WHEREo.amount>1000;
```
2.UNIONvsUNIONALL
性能差距核心来自"去重排序":UNION需排序去重,大数据量下耗时久;UNIONALL无额外操作,性能更优。
注意:UNION要求合并的多个查询结果集,对应列的数据类型必须兼容。如果类型差异较大,可以使用`CAST()`函数转换:
```sql
方案1:将product_1的INT类型price转为VARCHAR
SELECT
id,
name,
CAST(priceASVARCHAR(20))ASprice转换数值为字符串
FROMproduct_1
UNION
SELECT
id,
name,
price本身就是VARCHAR,无需转换
FROMproduct_2;
方案2:将product_2的VARCHAR类型price转为INT
SELECT
id,
name,
price本身就是INT,无需转换
FROMproduct_1
UNION
SELECT
id,
name,
CAST(priceASUNSIGNEDINT)ASprice转换字符串为无符号整数
FROMproduct_2;
```
3.优先级选型建议
多表关联:优先用JOIN(INNER/LEFT),搭配索引保障性能稳定。
简单二次筛选:用非相关子查询(内层结果集小),兼顾逻辑清晰。
同结构结果合并:优先UNIONALL,杜绝无意义去重排序。
禁用场景:大数据量相关子查询、无过滤条件的CROSSJOIN、FULLJOIN模拟。
三、通用优化技巧
优化的核心是减少扫描行数、避免全表扫描、杜绝冗余排序和临时表。
1.索引优化(核心中的核心)
JOIN查询:对关联字段创建B+树索引。对于LEFTJOIN优先给右表关联字段建索引,INNERJOIN可双表均建。
子查询:对内层where条件过滤和关联的字段建索引,减少扫描行数。
排序/分页:对ORDERBY、LIMIT的字段建索引,避免文件排序(Usingfilesort)。
2.语句优化
拒绝SELECT:仅查询必要字段,减少传输和内存开销。
大数据量用EXISTS替代IN:EXISTS匹配到一条即返回,效率优于遍历全量结果集的IN。
拆分复杂JOIN:超过3张表的JOIN拆分为分步查询,减小临时表规模。
避免WHERE函数操作:如`DATE(create_time)='20240101'`会致索引失效,改为`create_timeBETWEEN'2024010100:00:00'AND'2024010123:59:59'`。
3.驱动表选择(JOIN性能关键)
驱动表是JOIN时先加载的表,直接决定循环次数。
3.1自动确定逻辑
INNERJOIN:无固定驱动表,优化器按执行成本(扫描行数、索引效率)选择,通常小表驱动大表。
LEFT/RIGHTJOIN:驱动表固定(LEFTJOIN左表为驱动表,RIGHTJOIN右表为驱动表)。
3.2核心选择原则
无索引场景:小表驱动大表,大幅减少循环次数。
有索引场景:被驱动表索引匹配耗时骤降(O(logN)),表大小影响减弱,仍优先小表驱动。
超大表例外:千万级无索引表关联,优先建索引或分表分库。
3.3手动调整(优化器误判时)
用`STRAIGHT_JOIN`强制驱动表:`SELECTFROM小表STRAIGHT_JOIN大表ON关联条件;`
更新统计信息:执行`ANALYZETABLE表名;`
4.数据量优化
分页查询:用索引定位(`WHEREid>100LIMIT10`),避免`LIMIT100000,10`(扫描前100010行)。
分表分库:千万级+超大表(如订单表)按时间/业务分表,减少单表JOIN数据量。
5.执行计划分析(用EXPLAIN定位瓶颈)
关注type字段:至少达到range(范围扫描),理想为ref(索引查找)、eq_ref(唯一索引查找)。
规避性能杀手:Usingfilesort(文件排序)、Usingtemporary(临时表),需通过索引优化解决。
四、总结
MySQL查询优化的核心是"理解执行逻辑,适配业务场景",我们可以总结经验如下:
1.JOIN是多表关联首选,重点优化索引和驱动表。
2.子查询慎用于大数据量,相关子查询优先改写为JOIN。
3.UNION系列优先选UNIONALL,拒绝无意义开销。
4.所有优化需以EXPLAIN执行计划为依据,而非单纯依赖经验。
性能优化无银弹,需结合数据量、索引、业务综合权衡。日常开发养成用EXPLAIN分析查询的习惯,才能从根源规避慢查询。

一家致力于优质服务的软件公司
8年互联网行业经验1000+合作客户2000+上线项目60+服务地区

关注微信公众号
