8.2.2.1 使用半联接转换优化子查询,派生 table 和视图引用

半联接是准备时转换,它启用多种执行策略,例如 table 提取,重复删除,首次匹配,松散扫描和实现。如本节所述,优化器使用半联接策略来改善子查询的执行。

对于两个 table 之间的内部联接,该联接从一个 table 返回一行的次数是另一 table 中存在匹配项的次数。但是对于某些问题,唯一重要的信息是是否存在匹配项,而不是匹配数。假设存在名为classroster的 table,这些 table 分别列出了类 table 中的类和类花名册(每个类的学生人数)。要列出实际招收学生的类,您可以使用以下联接:

SELECT class.class_num, class.class_name
FROM class INNER JOIN roster
WHERE class.class_num = roster.class_num;

但是,结果为每个注册学生列出一次每个类。对于所提出的问题,这是不必要的信息重复。

假设class_numclasstable 中的主键,则可以通过使用SELECT DISTINCT来抑制重复,但是先生成所有匹配的行仅用于稍后消除重复是无效的。

可以通过使用子查询获得相同的无重复结果:

SELECT class_num, class_name
FROM class
WHERE class_num IN (SELECT class_num FROM roster);

在这里,优化器可以识别IN子句要求子查询仅返回rostertable 中每个类号的一个实例。在这种情况下,查询可以使用半联接。也就是说,该操作仅返回class中的每一行的一个实例,该实例与roster中的行匹配。

外部查询规范中允许使用外部联接和内部联接语法,并且 table 引用可以是基 table,派生 table 或视图引用。

在 MySQL 中,子查询必须满足以下条件才能作为半联接进行处理:

SELECT ...
FROM ot1, ...
WHERE (oe1, ...) IN (SELECT ie1, ... FROM it1, ... WHERE ...);

在这里,ot_iit_itable 示查询的外部和内部部分中的 table,oe_iie_itable 示引用外部和内部 table 中的列的 table 达式。

子查询可以是相关的或不相关的。除非已使用ORDER BY,否则允许DISTINCT,也允许LIMIT

如果子查询满足上述条件,MySQL 会将其转换为半联接并从以下策略中进行基于成本的选择:

可以使用以下optimizer_switch系统变量标志来启用或禁用这些策略中的每一个:

默认情况下启用这些标志。参见第 8.9.2 节“可切换的优化”

优化器将视图和派生 table 的处理差异最小化。这会影响使用STRAIGHT_JOIN修饰符的查询以及带有IN子查询的视图,该视图可以转换为半联接。以下查询说明了这一点,因为处理中的更改导致转换中的更改,从而导致不同的执行策略:

CREATE VIEW v AS
SELECT *
FROM t1
WHERE a IN (SELECT b
           FROM t2);

SELECT STRAIGHT_JOIN *
FROM t3 JOIN v ON t3.x = v.a;

优化器首先查看视图,并将IN子查询转换为半联接,然后检查是否有可能将视图合并到外部查询中。因为外部查询中的STRAIGHT_JOIN修饰符防止半联接,所以优化器拒绝合并,从而导致使用物化 table 进行派生 table 评估。

EXPLAIN输出指示使用半连接策略,如下所示:

首页