1. IN/EXISTS 改写为 JOIN 的等价条件与性能差异?
请说明 IN/EXISTS 子查询改写为 JOIN 的等价条件,以及它们在性能上的差异?
- 子查询展开为 JOIN 的等价条件(无重复、无聚合)。
- 优化器通常自动展开,但需注意去重与 NULL 语义。
- 性能差异取决于优化器能否正确转换。
IN 与 EXISTS 子查询在语义上都可以改写为 JOIN,但改写时需保证等价:IN 要求子查询结果不重复(若子查询可能重复行,结果会重复,需加 DISTINCT 或用 EXISTS 替代);EXISTS 只做存在性判断,天然去重;改写为 JOIN 时若子查询列有重复,需用 DISTINCT JOIN 或改用 EXISTS。现代优化器(PostgreSQL、MySQL 8.0)通常能自动将可展开的 IN/EXISTS 子查询改写为 JOIN(称为子查询展开/unnesting),从而共享索引、连接优化。性能差异主要体现在:计数上 IN 需去重、EXISTS 只需半连接;若优化器无法展开(如子查询含聚合、LIMIT、相关引用复杂),则退化为逐行执行子查询,性能差。手工改写时用 EXISTS 或 JOIN 更易被优化。
关键在"等价性"与"能否展开"。优化器能自动展开时两者接近,不能展开时 EXISTS(半连接)通常优于 IN 的逐行执行。
-- 改写为例
SELECT * FROM a WHERE id IN (SELECT a_id FROM b);
SELECT a.* FROM a JOIN (SELECT DISTINCT a_id FROM b) b ON a.id = b.a_id;
SELECT * FROM a WHERE EXISTS (SELECT 1 FROM b WHERE b.a_id = a.id);