☰
MySQL连接查询全解析:内连接、外连接与ON/WHERE条件实战避坑
2026/10/7 3:45:06 网站建设 项目流程

老实说,做后端开发这些年,天天跟 MySQL 打交道,如果让我挑一个“看着简单、用着犯浑、出问题还贼隐蔽”的知识点,连接查询绝对排前三。尤其是内连接和外连接,很多刚入行的同事张口就是 “inner join 取交集、left join 取左边全部”,可真到了写 SQL 的时候,ON 条件该放哪、WHERE 能不能乱加、RIGHT JOIN 为什么没人用、几千万行的表连接怎么跑得快,一问一个准。

今天就把这块彻底聊透。我会从最底层的逻辑开始拆,然后带你把内连接、外连接、交叉连接、自连接全部过一遍,每个场景都会给可直接抄的 SQL 示例,中间穿插我在实际项目里踩过的坑和排查思路。看完这篇,你写连接查询就不会再靠猜了。

1. 连接查询到底在解决什么问题

1.1 我们为什么要“拆表”和“连表”

先聊一个看似废话、其实很多人没想明白的问题:为什么好好的数据要拆成多张表,然后查询的时候再费劲连起来?直接一张大表不香吗?

你想一个最简单的电商订单场景。订单表里存了用户 ID、商品 ID、金额、下单时间,如果按 “一张表存所有” 的思路,用户下单多少次,用户的姓名、电话、地址就要跟着订单重复存多少次。万一用户改了手机号,你得把历史订单里所有这个用户的记录全部 UPDATE 一遍,漏一条数据就发疯了。

所以正规设计都会拆:用户表(user)只存用户信息,订单表(order)只存订单和关联 ID,商品表(product)只存商品信息。要查 “谁在什么时候买了什么东西”,就得通过关联字段(一般是主键和外键)把多张表临时拼起来——这个动作就是连接查询。

连接查询的本质,就是基于两张表的关联字段,把它们的行按指定规则组合起来,生成一张临时的“宽表”。而组合的规则,就是今天的主角:内连接(INNER JOIN)和外连接(OUTER JOIN)。搞懂了规则,你写的每一句 JOIN 才是有意义的,而不是照着模板瞎抄。

1.2 内连接和外连接的本质区别一句话版

先用一句话把概念钉死,后面再展开:

  • 内连接(INNER JOIN):只保留两张表里同时能匹配上的行,匹配不上的直接丢弃。
  • 外连接(LEFT/RIGHT/FULL JOIN):先保住某一边(或两边)的全部行,另一边的数据能匹配上就填进来,匹配不上就用 NULL 补齐。

如果你对集合运算有概念,可以这么类比:内连接相当于两个集合的交集;左外连接相当于“左集合的全部 + 与右集合的交集部分,右集合缺失的用空值占位”;右外连接则反过来;全外连接相当于两个集合的并集。

这个区别看起来很简单,但实际写 SQL 时会有大量“看似逻辑一样、结果完全不同”的坑,尤其是 ON 条件和 WHERE 条件的摆放位置,我后面会专门用一整节讲。

2. 内连接(INNER JOIN)实操拆解

2.1 基本语法和最简单的例子

内连接的语法有两种,效果一样:

-- 写法一:显式 JOIN SELECT * FROM user u INNER JOIN order o ON u.id = o.user_id; -- 写法二:隐式连接(逗号 + WHERE) SELECT * FROM user u, order o WHERE u.id = o.user_id;

我强烈建议你只用写法一。老项目里那种FROM a, b WHERE a.x = b.y的写法虽然也能跑,但表一旦多起来,过滤条件和连接条件混在一个 WHERE 里,看得人头皮发麻,而且很多入门教程已经不再推荐这种写法了。

下面我用一组最简单的示例数据来展示内连接的完整行为。假设有两张表:

-- 用户表 CREATE TABLE `user` ( `id` INT PRIMARY KEY, `name` VARCHAR(20) ); INSERT INTO `user` VALUES (1, '张三'), (2, '李四'), (3, '王五'); -- 订单表 CREATE TABLE `order` ( `id` INT PRIMARY KEY, `user_id` INT, `amount` DECIMAL(10,2) ); INSERT INTO `order` VALUES (101, 1, 99.00), (102, 1, 199.00), (103, 2, 59.00);

注意看,王五(id=3)没有任何订单,订单里也没有 user_id=3 的记录。执行内连接:

SELECT u.id, u.name, o.id AS order_id, o.amount FROM `user` u INNER JOIN `order` o ON u.id = o.user_id;

结果只有三行:张三的两笔订单、李四的一笔订单。王五因为没有匹配订单,整行被丢弃了。这就是内连接:必须两边都匹配上,否则不出现。

2.2 内连接的过滤条件该放 ON 还是 WHERE

这是内连接里最容易踩的第一个坑。

先记住一个结论:对于 INNER JOIN 来说,过滤条件放 ON 和放 WHERE,结果是一样的。

-- 放 ON:先把订单金额 >= 100 的关联上 SELECT u.name, o.amount FROM `user` u INNER JOIN `order` o ON u.id = o.user_id AND o.amount >= 100; -- 放 WHERE:先全量连接,再过滤金额 SELECT u.name, o.amount FROM `user` u INNER JOIN `order` o ON u.id = o.user_id WHERE o.amount >= 100;

上面两条 SQL,结果完全一致。因为内连接本身就会丢弃不匹配的行,所以无论你在哪一步做过滤,最终留下的都是“关联成功且满足金额条件”的行。

但在外连接里,这个位置就生死攸关了,强烈建议你现在先记住:ON 决定“连接哪些行”,WHERE 决定“连接之后保留哪些行”。到外连接那一节我会展示完全不同的结果。

2.3 多表内连接怎么写才不乱

内连接可以同时连接多张表,比如订单要关联用户和商品,就是三张表连接:

SELECT u.name AS 用户名, p.product_name AS 商品名, o.amount AS 金额 FROM `order` o INNER JOIN `user` u ON o.user_id = u.id INNER JOIN `product` p ON o.product_id = p.id;

三种易错点提醒:

  1. 连接顺序会影响性能,不影响结果。MySQL 的优化器通常会帮你重新调整连接顺序,但如果你需要手控,一般原则是“小表驱动大表”,这个后面“性能优化”部分再细说。
  2. 不要漏掉连接条件。漏了一个 ON 条件,三张表就会交叉组合,行数爆炸式增长,往往就是笛卡尔积的灾难。
  3. 每张表最好起别名,尤其自连接时必须起别名,否则字段名一冲突,SQL 直接报错或者结果错乱。

3. 外连接(LEFT / RIGHT JOIN)实操拆解

3.1 LEFT JOIN:左表保底,右表填空

左外连接(LEFT OUTER JOIN,一般直接写 LEFT JOIN)的含义是:左表的每一行都会出现在结果里,右表有匹配就带出来,没有匹配就用 NULL 补齐。

还用上面那两张表:

SELECT u.id, u.name, o.id AS order_id, o.amount FROM `user` u LEFT JOIN `order` o ON u.id = o.user_id;

结果会多出一行王五的记录:user 表的信息正常显示,order 两列全是 NULL。这就是左连接的核心行为。

这在实际业务里太常用了。比如你要统计“每个用户的订单总额,没有订单的也要显示 0”,直接 GROUP BY + LEFT JOIN 就能一次搞定:

SELECT u.name, COUNT(o.id) AS 订单数, IFNULL(SUM(o.amount), 0) AS 总金额 FROM `user` u LEFT JOIN `order` o ON u.id = o.user_id GROUP BY u.id, u.name;

注意这里两个细节:

  • 统计订单数要用COUNT(o.id),而不是COUNT(*)。因为左连接后,没订单的用户那行 order 字段全是 NULL,COUNT(o.id)会跳过 NULL,而COUNT(*)会把它数成 1,导致订单数错误。
  • 总金额要用IFNULL(SUM(o.amount), 0),否则没有订单的用户合计出来是 NULL,前端展示就会显示空白而不是 0。

3.2 RIGHT JOIN:为什么实际项目里几乎没人用

右外连接(RIGHT JOIN)就是把左表右表调换位置,语法上完全对称:

SELECT u.id, u.name, o.id AS order_id, o.amount FROM `order` o RIGHT JOIN `user` u ON u.id = o.user_id;

这条 SQL 和上面那条 LEFT JOIN 结果一模一样。

那为什么实际项目里大家基本只写 LEFT JOIN,很少写 RIGHT JOIN?两个原因:

  1. 可读性差。中文阅读习惯是从左往右理解,“主表在左边、附表在右边”更符合思维惯性。你写 RIGHT JOIN,看代码的人还得在脑子里把表换一下位置。
  2. ** RIGHT JOIN 在很多老版数据库里支持不彻底**,优化器处理也可能没 LEFT JOIN 成熟(现代 MySQL 8.0 其实没问题,但习惯已经形成了)。

我的建议很明确:统一只写 LEFT JOIN。如果需求逻辑上是以某张表为主表,但主表写在 ON 右侧,你完全可以调整成 LEFT JOIN 的写法,让主表回到左侧。代码规范里明确这一点,团队协作时大家读 SQL 会轻松很多。

3.3 全外连接(FULL JOIN)在 MySQL 里的实现方案

标准 SQL 里有 FULL OUTER JOIN,取“两边全部行,匹配不上用 NULL 补齐”。但 MySQL 从一开始就不支持 FULL JOIN,至今(8.0 版本)也没有。如果你需要这个语义,只能用 LEFT JOIN UNION RIGHT JOIN 来模拟。

假设你要查“所有用户和所有订单全展示,没匹配的都补 NULL”,SQL 长这样:

SELECT u.id AS user_id, u.name, o.id AS order_id, o.amount FROM `user` u LEFT JOIN `order` o ON u.id = o.user_id UNION SELECT u.id AS user_id, u.name, o.id AS order_id, o.amount FROM `user` u RIGHT JOIN `order` o ON u.id = o.user_id;

UNION 会自动去重,所以相当于把左连接结果和右连接结果合并,两边的 “未匹配行” 都会被保留,就拼出了全连接的效果。

实际使用中,这种需求其实很少见。更多时候,你只是想知道“两边不匹配的数据到底有哪些”,这种场景用连接 + 空值判断就够了。比如查“没有任何订单的用户”:

SELECT u.* FROM `user` u LEFT JOIN `order` o ON u.id = o.user_id WHERE o.id IS NULL;

这里就是一个典型的连接玩法:连接,然后筛选出右表为空的行。平时写“在 A 表但不在 B 表”的数据,就是你最顺手的一个套路。

3.4 重点:ON 条件和 WHERE 条件在外连接中的致命区别

这一小节才是外连接真正的考点,也是最容易让结果错得莫名其妙的地方。

还是之前那个例子,现在我要查“所有用户以及他们金额大于等于 100 的订单”。先看第一种写法,过滤条件放 ON:

SELECT u.id, u.name, o.id AS order_id, o.amount FROM `user` u LEFT JOIN `order` o ON u.id = o.user_id AND o.amount >= 100;

结果是:张三的 199 元订单出现,99 元订单因为不满足条件被过滤;李四的 59 元订单被过滤;王五依然作为 NULL 行保留。

再看第二种写法,过滤条件放 WHERE:

SELECT u.id, u.name, o.id AS order_id, o.amount FROM `user` u LEFT JOIN `order` o ON u.id = o.user_id WHERE o.amount >= 100;

结果:王五那行 NULL 数据直接消失了,因为 NULL >= 100 判断结果是 NULL,不是真值,被 WHERE 淘汰了。连张 99 元订单也直接没了,只剩张三的 199 元订单。

看出问题了吗?同样是 “过滤金额 >= 100”,放 ON 保留左表全部行(王五还在),放 WHERE 就把左表的空行也杀了。实际开发里如果需求是“主表全部展示,附带满足条件的附表信息”,过滤条件必须放 ON;如果需求是“只要满足条件的关联记录”,那才放 WHERE。

我见过太多线上事故就是这两行位置写反了。以后你看到一段 LEFT JOIN 的 SQL,先别急着看结果,先判断过滤条件写在哪个子句里,你就能快速定位它真正想表达的逻辑。

4. 从内外连接延伸出去的实操技巧

4.1 CROSS JOIN 交叉连接:最危险也最有用

交叉连接(CROSS JOIN)不加任何 ON 条件,直接把一张表的每一行和另一张表的每一行做组合,结果行数等于两张表行数的乘积,也就是笛卡尔积。

SELECT * FROM `user` u CROSS JOIN `order` o;

这张表有 3 行用户、3 行订单,结果就是 9 行。

实战里这个连接用得少,但有两个场景非常典型:

  1. 生成测试数据。比如你要给每个用户造一批随机订单,用 CROSS JOIN 配合日期函数、随机函数,几秒钟就能铺出一大批测试数据。
  2. 生成行列交叉报表。比如日期表(月份列表)和部门表做 CROSS JOIN,然后左连接业务数据,就能把没产生业务的月份也用 0 补齐。

需要特别警告的是,千万别在没写 ON 条件的时候漏写 WHERE 连接条件。尤其那种顺手从别的 SQL 复制过来、把连接条件删了的情况,一旦两张表都是百万级,笛卡尔积会直接让数据库 CPU 爆掉,查询卡死到天荒地老。建议所有 CROSS JOIN 都在开发环境先 EXPLAIN 看下扫描行数。

4.2 自连接:同一张表和自己连接

自连接就是一张表跟自己 JOIN,核心在于用两个不同的别名把同一张表当成两张表来用。最经典的场景就是员工表里存了上级的 ID,你要查每个员工和它的上级姓名:

假设表结构:

CREATE TABLE emp ( id INT PRIMARY KEY, name VARCHAR(20), manager_id INT ); INSERT INTO emp VALUES (1, '赵总', NULL), (2, '钱经理', 1), (3, '孙组长', 2), (4, '李员工', 3);

查询每个员工以及他上级的名字:

SELECT e.name AS 员工姓名, m.name AS 上级姓名 FROM emp e LEFT JOIN emp m ON e.manager_id = m.id;

注意几点:

  • 必须用别名,而且两个别名含义要清楚,我习惯e表示当前行(员工),m表示 manager(上级)。
  • 这里用 LEFT JOIN 而不是 INNER JOIN,是为了把赵总(没有上级)也保留下来;如果用 INNER JOIN,赵总就被丢了。
  • 自连接也可以做内连接、做多级连接(比如查上上级),只要你想清楚“每一步连接的两端分别是谁”。

自连接的另一个高频场景是“找出重复数据”。例如有一张访问日志表,你想找同一个用户在同一分钟内是不是有两条记录,自连接 + 时间窗口就能完成:

SELECT DISTINCT a.id, a.user_id FROM visit_log a INNER JOIN visit_log b ON a.user_id = b.user_id AND a.id != b.id AND ABS(TIMESTAMPDIFF(SECOND, a.visit_time, b.visit_time)) < 60;

这种写法在数据校验、清洗场景里非常实用。

4.3 USING 和 NATURAL JOIN:写起来爽,读起来可能想骂人

如果两张表连接字段名恰好一样,比如都是user_id,那么可以用 USING 简化连接条件:

SELECT * FROM `order` o INNER JOIN `user` u USING (user_id); -- 等价于 SELECT * FROM `order` o INNER JOIN `user` u ON o.user_id = u.user_id;

更懒的写法是 NATURAL JOIN,它自动找两表中同名的列做连接:

SELECT * FROM `order` o NATURAL JOIN `user` u;

我个人的态度是:USING 可以偶尔用,NATURAL JOIN 尽量别用。原因很简单,NATURAL JOIN 会自动把所有同名同类型的字段作为连接条件,一旦表结构里有两个同名字段(比如都有remark列),它就按remark也连接,结果分分钟错乱。而且这种隐式行为让别人读代码时完全不知道连接条件是什么,调试成本极大。宁可多写几个字符,把 ON 条件写明确,这是对自己和队友负责。

4.4 JOIN 和子查询怎么选

有个高频问题:一张订单表、一张用户表,想知道每个用户下过哪些订单。用 JOIN 和用子查询都能写:

-- 写法 A:JOIN SELECT u.*, o.* FROM `user` u LEFT JOIN `order` o ON u.id = o.user_id; -- 写法 B:关联子查询 SELECT u.*, (SELECT o.amount FROM `order` o WHERE o.user_id = u.id ORDER BY o.amount DESC LIMIT 1) AS 最大订单金额 FROM `user` u;

这两种写法表面上是“都能查出来”,但性能特征完全不同。JOIN 是做一次连接再投影,子查询在 MySQL 5.x 时代往往被实现成“外部每一行执行一次子查询”,相当于循环查 N 次,代价很高;MySQL 8.0 的优化器对子查询的改写能力强了很多,但依然要具体 SQL 具体分析。

我在实际项目里的选型原则:

  1. 需要返回子表多列数据、或者需要对子表做聚合统计,优先 JOIN。
  2. 只需要取子表某个聚合值或某条最新记录,优先考虑子查询(配合 LIMIT 1 或者窗口函数)。
  3. 不管选哪种,线上大表一定要 EXPLAIN 看一眼执行计划,别靠感觉决策。

5. 连接查询的性能问题和优化策略

5.1 连接顺序:谁当驱动表影响很大

多表连接时,MySQL 会选一张表先访问,这张表叫驱动表(也叫外表),然后用它去匹配其他表。优化器默认会基于统计信息选一版成本最低的顺序,但统计信息不准或者 JOIN 条件复杂时,它也可能选错。

常见经验法则是:小表驱动大表。也就是用行数少的表当驱动表,先拿出小结果集,再逐行去大表里匹配,这样匹配次数更少。如果搞反了,用大表驱动小表,IO 和比较次数都会明显上升。

强制指定连接顺序可以用STRAIGHT_JOIN:

SELECT STRAIGHT_JOIN u.*, o.* FROM `user` u INNER JOIN `order` o ON u.id = o.user_id;

STRAIGHT_JOIN会告诉优化器:按我 FROM 后面写的顺序来查,不要自己调。但这不是什么银弹,如果你的连接顺序本身就选错了,强制只会更慢。所以更多时候你该做的是:看 EXPLAIN、检查连接字段统计信息、确保关联字段有索引,而不是硬碰硬调顺序。

5.2 连接字段必须有索引,数据类型必须一致

内连接、外连接的本质是用关联字段逐行匹配,驱动的每一行都要去被驱动表里找对应行。如果被驱动表的连接字段没索引,那就退化成“每次全表扫”。几万行的表还好,几百万行的表就是灾难。

建索引的细节:

-- 在订单表的 user_id 上建索引 ALTER TABLE `order` ADD INDEX idx_user_id (user_id);

另外还要强调一句:连接字段的数据类型最好完全一致。int对varchar,或者char对varchar,看着长得差不多,但索引优化时可能失效,导致隐式转换、全表扫描。我排查过一个慢查询,就是 A 表 user_id 是int,B 表 user_id 是varchar,怎么加索引都走不上,最后统一字段类型直接解决。

5.3 大表连接:几千万行到底怎么扛

热搜词里出现了“几千万行大表”,这块也得提一嘴,因为连接查询在数据量上来之后,会暴露完全不同的麻烦。

几千万行的表做 JOIN,三个关键点:

  1. 先过滤,后连接。这个有多重要怎么强调都不过分。如果你在 WHERE 里筛选,MySQL 优化器通常会把过滤条件下推到连接之前;但如果你在 ON 里写子查询、在 SELECT 里写复杂函数,过滤就很难被提前。比如下面这种写法,子查询会先缩到几百行再连接,性能会好很多:
SELECT * FROM `order` o INNER JOIN ( SELECT id FROM `user` WHERE status = 1 ) u ON o.user_id = u.id;
  1. 只取需要的列,不要用SELECT *。JOIN 过程中参与的行越宽,内存和临时表的压力越大。你不该为了让 SQL 看起来简单,把所有列都拉出来,最后前端只用两列。

  2. 如果做了聚合 + 连接,结果集仍然很大,考虑把聚合结果落成临时表或物化视图。我最常用的一个技巧:先用子查询把某张表按用户维度聚合好,再和另一张表 JOIN,这样 JOIN 的数据量直接从“行数”变成“用户数”,数量级完全不一样。

5.4 连接查询的 EXPLAIN 到底怎么看

每次写复杂 JOIN 之前,我都会先跑一遍:

EXPLAIN SELECT ...

重点看这么几个字段:

  • type:从好到差依次是 system > const > eq_ref > ref > range > index > ALL。出现 ALL,说明有表在被迫全表扫,十有八九是没走索引。
  • key:实际用的索引。如果为 NULL,赶紧检查连接字段和 WHERE 字段有没有索引。
  • rows:预估要扫描的行数。行数乘积太大,就要考虑调整连接顺序或加过滤条件。
  • Extra:如果出现Using temporary; Using filesort,说明可能要建临时表或文件排序,数据量大时性能会很差。

这块我不展开太多,但因为连接查询最容易出现的就是全表扫描和临时表,EXPLAIN 是排查慢 JOIN 的第一工具,习惯一定要养成。

6. 常见问题与排查技巧实录

6.1 常见问题速查表

症状可能原因排查/解决建议
LEFT JOIN 结果比预期少过滤条件写在了 WHERE 里,把左表空匹配行杀掉了确认业务语义,需要保留主表全部行时把过滤条件挪到 ON
结果行数爆炸漏了 ON 条件或连接条件写错,产生笛卡尔积用 EXPLAIN 看 rows 乘积值,检查 ON 条件是否完整
COUNT 统计结果偏大或偏小用 COUNT(*) 统计外连接中的右表字段改成 COUNT(右表标识列/主键列),如 COUNT(o.id)
LEFT JOIN 关联的右表有重复数据右表对应键值不唯一,导致一对多重复先用子查询对右表按唯一键聚合,再 JOIN
右边表匹配不上的行出现 NULL,业务无法区分需求上需要区分“无订单”和“订单金额为 NULL”用 IFNULL 缺省值,或加一个 is_deleted 之类的状态字段
JOIN 查询特别慢连接字段无索引 / 数据类型不一致建索引、统一字段类型、用 EXPLAIN 检查 type 字段
多表 JOIN 结果随机性变大优化器选择连接顺序不稳定使用 STRAIGHT_JOIN 强制顺序(但须验证)

6.2 我踩过的几个经典坑

第一个坑:LEFT JOIN 之后做 WHERE 过滤导致主表行丢失。有一回统计“每个销售负责的客户是否都有回款记录”,我在 WHERE 里加了AND 回款金额 > 0,结果所有没有回款的销售直接消失了,报表数字明显不对。后来才意识到,应该把金额条件写进 ON 子句,才符合“销售全部展示,附带有效回款”的语义。

第二个坑:连接字段的字符集排序规则不一致。两张表的 user_id 一个是 utf8mb4_general_ci,一个是 utf8mb4_0900_ai_ci,连 ON 条件相等时,索引可能会对比失败。这个报错不会直接告诉你“字符集不匹配”,而是表现为查询慢、索引失效、甚至报Illegal mix of collations。排查到这一步时,很多人会忽略。

第三个坑:用 JOIN 更新/删除数据时忽略了重复匹配。比如:

UPDATE `order` o INNER JOIN `user` u ON o.user_id = u.id SET o.flag = 1;

如果 user 表里存在重复的 id(万一是历史脏数据),同一个订单会被更新多次,虽然是幂等操作,看上去没异常,但如果你在 SET 里写的是o.amount = o.amount + 1这种非幂等逻辑,结果就完全错了。所以 JOIN 更新前最好先用 SELECT 查一下关联后会不会膨胀行数。

6.3 一个完整的排查案例

曾有同事报障:一条 LEFT JOIN 的 SQL,原本一秒跑完,现在要三十秒。

我先看 EXPLAIN,发现驱动表的连接字段u.id是主键没问题,被驱动表o.user_id也有索引,但 type 是 ALL,说明优化器没走索引。再深挖,发现o.user_id虽然建了索引,但字段类型是varchar(20),而u.id是int,MySQL 把 varchar 隐式转换成数值类型比对,导致o.user_id上的索引失效。解决方案就是把订单表的 user_id 改成 int 类型,并统一两边数据的格式,跑完直接恢复到一秒内。

这个案例非常典型。很多所谓“慢 JOIN”根本不是数据量的问题,而是字段类型不一致造成的索引失效。遇到慢查询,先看字段类型、再看索引、最后才是调优化器。

7. 最后分享几个我的实操习惯

如果你准备把今天这些内容消化到自己的代码里,我建议从这几个习惯开始:

第一,每条 JOIN 都写清楚 ON 条件,绝不用 NATURAL JOIN。哪怕连接字段名相同,也用明确条件,这样未来别人接手时不会靠猜。

第二,写外连接时,先问自己一句:哪张表是主表?想清楚这个问题,再决定 LEFT JOIN 还是 RIGHT JOIN。实际工作中主表在左侧、附表在右侧,读起来最舒服。

第三,大表 JOIN 前先跑 EXPLAIN。甚至我会把一个 JOIN 查询先改成 SELECT COUNT(*) 的形态,确认它会扫描多少行,再决定要不要加过滤条件。等数据量大到一定程度,好的查询习惯比任何 SQL 技巧都重要。

第四,多表连接时如果逻辑特别复杂,不如拆成临时表分步查。有时候一段 8 张表连接的 SQL 摆在那里,看着霸气,实际上排查问题的成本极高。我宁可先用中间表算好部分结果,再 JOIN 最后一张,性能不一定差,可读性却有质的提升。

连接查询这东西,说到底是“数据关系”的映射,你脑子里的业务关系有多清楚,写出来的 JOIN 就有多可靠。多动手、多看执行计划、多复盘线上慢查询,用不了多久你就能完全驾驭它。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询