SELECT class_num, class_name
FROM class
WHERE class_num IN
(SELECT class_num FROM roster);
在这里,优化器可以识别该
IN
子句要求子查询从表中返回每个类编号的一个实例
roster
。在这种情况下,查询可以使用
半连接
;也就是说,一种操作只返回
class
与 中的行匹配
的每一行的一个实例
roster
。
EXISTS
子查询谓词的语句等同于前面包含
IN
子查询谓词的语句:
SELECT class_num, class_name
FROM class
WHERE EXISTS
(SELECT * FROM roster WHERE class.class_num = roster.class_num);
在 MySQL 8.0.16 及更高版本中,任何带有
EXISTS
子查询谓词的语句都与具有等效子查询谓词的语句一样受到相同的半连接转换
从 MySQL 8.0.17 开始,以下子查询被转换为反连接:
NOT IN (SELECT ... FROM ...)
NOT EXISTS (SELECT ... FROM ...)
.
IN (SELECT ... FROM ...) IS NOT TRUE
EXISTS (SELECT ... FROM ...) IS NOT
TRUE
.
IN (SELECT ... FROM ...) IS FALSE
EXISTS (SELECT ... FROM ...) IS FALSE
.
简而言之,对
IN
(SELECT ... FROM ...)
or形式的子查询的任何否定
EXISTS (SELECT ...
FROM ...)
都会转换为反连接。
反连接是一种仅返回没有匹配项的行的操作。考虑此处显示的查询:
SELECT class_num, class_name
FROM class
WHERE class_num NOT IN
(SELECT class_num FROM roster);
该查询在内部被重写为 antijoin
,它返回中
与 中任何行
都不
匹配
SELECT class_num, class_name FROM class ANTIJOIN
roster ON class_num
的每一行的一个实例。这意味着,对于 中的每一行
,只要在 中找到匹配项
就可以丢弃
该行。
class
roster
class
roster
class
如果被比较的表达式可以为空,则在大多数情况下不能应用反连接转换。此规则的一个例外是
(... NOT IN (SELECT ...)) IS NOT
FALSE
它的等价物
(... IN (SELECT
...)) IS NOT TRUE
可以转换为反连接。
外部查询规范允许外部连接和内部连接语法,表引用可以是基表、派生表、视图引用或公共表表达式。
在 MySQL 中,子查询必须满足这些条件才能作为半连接处理(或者,在 MySQL 8.0.17 及更高版本中,如果
NOT
修改子查询则为反连接):
它必须是出现在
or子句顶层的 、 或谓词的
IN
一部分
=
ANY
,可能作为
表达式中的术语。例如:
EXISTS
WHERE
ON
AND
SELECT ...
FROM ot1, ...
WHERE (oe1, ...) IN
(SELECT ie1, ... FROM it1, ... WHERE ...);
表示查询的外部和内部部分中的表,
表示引用外部表和内部表中的列的表达式。
ot_
i
it_
i
oe_
i
ie_
i
在 MySQL 8.0.17 及更高版本中,子查询还可以作为由 、 或 修饰的表达式
NOT
的
IS [NOT] TRUE
参数
IS [NOT] FALSE
。
它必须是一个
SELECT
没有
UNION
构造的。
它不得包含
HAVING
子句。
它不得包含任何聚合函数(无论是显式还是隐式分组)。
它不能有
LIMIT
子句。
该语句不得
STRAIGHT_JOIN
在外部查询中使用连接类型。
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
>
在 MySQL 8.0.21 及更高版本中,半连接转换也可以应用于
or子查询谓词的单表
UPDATE
或
,前提是该语句不使用
or ,并且优化器提示或通过允许半连接转换设置
DELETE
[NOT] IN
[NOT] EXISTS
ORDER BY
LIMIT
optimizer_switch