相关子查询慢是因为外层表每行都触发一次内层查询,10万行即执行10万次索引查找;JOIN通过批量连接、谓词下推和单次扫描优化性能,但需注意NULL处理、字符集一致性和索引匹配等改写陷阱。
因为子查询(尤其是相关子查询)在大表上容易触发“逐行执行”,而JOIN允许数据库优化器做批量连接、谓词下推和索引复用,避免重复扫描。
当外层表有 10 万行,子查询又依赖外层字段时,数据库大概率会执行 10 万次内层查询。比如:
SELECT u.name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) FROM user u;
哪怕 orders.user_id 有索引,每次仍要回表或走一次索引查找——不是“查一次索引,扫一遍数据”,而是“查 10 万次索引,每次扫一部分”。
select_type 显示 DEPENDENT SUBQUERY 就是典型信号LIMIT 可能掩盖问题,但本质未变等价的 LEFT JOIN 写法把逻辑从“对每行求值”转为“先关联再聚合”:
SELECT u.name, COUNT(o.id) FROM user u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.id;
这时优化器可以:
user 为驱动表(若它更小),或反过来选 orders(若过滤后更小)WHERE 条件(如 u.status = 1)下推到驱动表扫描阶段,提前减少输入行数orders.user_id 索引一次性定位所有匹配记录,而不是反复 seek关键点:orders 表只被顺序扫描(或索引范围扫描)一次,而不是 10 万次随机访问。
不是所有子查询都能无脑换 JOIN,以下情况必须小心:
NOT IN 含 NULL 时语义不同,得换成 LEFT JOIN ... WHERE o.id IS NULL,否则结果错SELECT (SELECT name FROM dept WHERE id = u.dept_id))改写后需 GROUP BY u.id 或加 DISTINCT,否则可能多出重复行utf8mb4 vs utf8)会导致 JOIN 失效,退化成全表比对——比子查询还慢JOIN 字段建索引,或者索引顺序不匹配 WHERE + JOIN 条件,优化器照样选错执行路径真正起作用的从来不是“用了 JOIN”,而是“JOIN 让优化器有了足够信息做全局决策”。别只改语法,盯着 EXPLAIN 里的 type、key、rows 和 Extra 看清楚到底扫了几行、用了什么索引、有没有临时表——这才是性能差异藏得最深的地方。
谈一谈企业为什么需要 `RAG` 知识库?
Claude Code配置智谱GLM-4.7 模型完整操作文档实用指南
第4周行业复盘总结:AI + Web3 四大落地场景的关键教训与可复用架构模式
非结构化数据用什么数据库好?Redis 和 MongoDB 如何选?阿里云瑶池数据库 Tair 与 Lindorm 方案
小米路由器4c(r4cm)是5g还是2.4g(小米路由器4C(R4CM)支持5G和2.4G吗)
毕业老学长给我留的最后一句话是:“AI 写的,别挂我名。” 我直接 神(Trae + Seed Evolving) 来!助我!