半连接是一种准备时间转换,支持多种执行策略,例如表拉出、重复剔除、首次匹配、松散扫描和物化。优化器使用半连接策略来改进子查询执行,如本节所述。
对于两个表之间的内部联接,联接从一个表返回行的次数与另一个表中的匹配项相同。但对于某些问题,唯一重要的信息是是否存在匹配项,而不是匹配项的数量。假设有一些表
class
,分别roster
列出了课程表中的班级和班级名册(每个班级的注册学生)。要列出实际有学生注册的班级,您可以使用此连接:
SELECT class.class_num, class.class_name
FROM class INNER JOIN roster
WHERE class.class_num = roster.class_num;
但是,结果会为每个注册学生列出每个班级一次。对于所问的问题,这是不必要的信息重复。
假设class_num
是表中的主键,class
使用 可以抑制重复
SELECT
DISTINCT
,但是先生成所有匹配行,然后才消除重复,效率很低。
使用子查询可以获得相同的无重复结果:
SELECT class_num, class_name
FROM class
WHERE class_num IN (SELECT class_num FROM roster);
在这里,优化器可以识别该
IN
子句要求子查询从表中返回每个类编号的一个实例
roster
。在这种情况下,查询可以使用半连接;也就是说,一种操作只返回
class
与 中的行匹配
的每一行的一个实例roster
。
外部查询规范允许使用外部连接和内部连接语法,表引用可以是基表、派生表或视图引用。
在 MySQL 中,子查询必须满足这些条件才能作为半连接处理:
它必须是出现在or 子句顶层的
IN
(or ) 子查询,可能作为表达式中的一个术语 。例如:=ANY
WHERE
ON
AND
SELECT ... FROM ot1, ... WHERE (oe1, ...) IN (SELECT ie1, ... FROM it1, ... WHERE ...);
在这里, and 表示查询的外部和内部部分中的表, 并且 表示引用外部表和内部表中的列的表达式。
ot_
i
it_
i
oe_
i
ie_
i
它不得包含
GROUP BY
orHAVING
子句。它不能隐式分组(它不能包含聚合函数)。
它不能有
ORDER BY
withLIMIT
。该语句不得
STRAIGHT_JOIN
在外部查询中使用连接类型。STRAIGHT_JOIN
修饰符不得存在 。外部表和内部表的总数必须小于连接中允许的最大表数。
子查询可以是相关的或不相关的。
DISTINCT
是允许的,
LIMIT
除非ORDER BY
也被使用。
如果子查询满足上述条件,MySQL 将其转换为半连接并从这些策略中做出基于成本的选择:
将子查询转换为连接,或使用表拉出并将查询作为子查询表和外部表之间的内部连接运行。表拉出将子查询中的表拉出到外部查询。
Duplicate Weedout:像连接一样运行半连接,并使用临时表删除重复记录。
FirstMatch:当扫描内表的行组合并且存在给定值组的多个实例时,选择一个而不是全部返回。这“快捷方式”扫描并消除了不必要行的产生。
LooseScan:使用索引扫描子查询表,该索引允许从每个子查询的值组中选择单个值。
将子查询具体化为用于执行连接的索引临时表,其中索引用于删除重复项。在将临时表与外部表连接时,该索引也可能在以后用于查找;如果不是,则扫描该表。有关物化的更多信息,请参阅 第 8.2.2.2 节,“使用物化优化子查询”。
可以使用以下optimizer_switch
系统变量标志启用或禁用这些策略中的每一个:
该
semijoin
标志控制是否使用半连接。如果
semijoin
启用,则firstmatch
、loosescan
、duplicateweedout
和materialization
标志可以更好地控制允许的半连接策略。如果
duplicateweedout
半连接策略被禁用,除非所有其他适用的策略也被禁用,否则它不会被使用。如果
duplicateweedout
被禁用,有时优化器可能会生成一个远非最佳的查询计划。这是由于贪婪搜索期间的启发式修剪而发生的,这可以通过设置来避免optimizer_prune_level=0
。
默认情况下启用这些标志。请参阅 第 8.9.2 节,“可切换优化”。
优化器将处理视图和派生表的差异最小化。这会影响使用
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
外部查询中的修饰符阻止半连接,优化器拒绝合并,导致使用物化表评估派生表。
EXPLAIN
输出表明使用了半连接策略,如下所示:
半连接表显示在外部选择中。对于扩展
EXPLAIN
输出,以下SHOW WARNINGS
显示的文本显示了重写的查询,其中显示了半连接结构。(请参阅第 8.8.3 节,“扩展的 EXPLAIN 输出格式”。)由此您可以了解哪些表已从半连接中提取出来。如果子查询转换为半连接,您可以看到子查询谓词消失了,它的表和WHERE
子句被合并到外部查询连接列表和WHERE
子句中。Duplicate Weedout 的临时表使用由
Start temporary
和End temporary
在Extra
列中指示。未被拉出且位于临时表所涵盖的输出 行 范围EXPLAIN
内的表。Start temporary
End temporary
rowid
FirstMatch(
列中的tbl_name
)Extra
表示连接快捷方式。LooseScan(
列中的m
..n
)Extra
表示使用 LooseScan 策略。m
并且n
是关键部件号。用于具体化的临时表由
select_type
值为 的MATERIALIZED
行和table
值为 的行指示。<subquery
N
>