相关文章推荐
半连接是一种准备时间转换,支持多种执行策略,例如表拉出、重复剔除、首次匹配、松散扫描和物化。优化器使用半连接策略来改进子查询执行,如本节所述。 对于两个表之间的内部联接,联接从一个表返回行的次数与另一个表中的匹配项相同。但对于某些问题,唯一重要的信息是是否存在匹配项,而不是匹配项的数量。假设有一些表 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 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 在外部查询中使用连接类型。

  • STRAIGHT_JOIN 修饰符不得存在 外部表和内部表的总数必须小于连接中允许的最大表数。 子查询可以是相关的或不相关的。在 MySQL 8.0.16 及更高版本中,decorrelation 查看 WHERE 子查询子句中简单相关的谓词作为 的参数 EXISTS ,并使得优化它成为可能,就好像它在 中使用一样 IN (SELECT b FROM ...) 。术语 trivially correlated 意味着谓词是一个等式谓词,它是 WHERE 子句中的唯一谓词(或与 结合 AND ),并且一个操作数来自子查询中引用的表,另一个操作数来自外部查询堵塞。

  • DISTINCT 允许但忽略 该关键字。半连接策略自动处理重复删除。

  • GROUP BY 允许但忽略子句,除非子查询还包含一个或多个聚合函数 允许但忽略子句,因为排序与半连接策略的 ORDER BY 评估无关。 如果子查询满足上述条件,MySQL 将其转换为半连接(或者,在 MySQL 8.0.17 或更高版本中,如果适用,则为反连接)并从这些策略中做出基于成本的选择: 将子查询转换为连接,或使用表拉出并将查询作为子查询表和外部表之间的内部连接运行。表拉出将子查询中的表拉出到外部查询。 Duplicate Weedout :运行半连接,就好像它是连接一样,并使用临时表删除重复记录。 FirstMatch :当扫描内表的行组合并且存在给定值组的多个实例时,选择一个而不是全部返回。这“快捷方式”扫描并消除了不必要行的产生。 LooseScan :使用索引扫描子查询表,该索引允许从每个子查询的值组中选择单个值。 将子查询具体化为用于执行连接的索引临时表,其中索引用于删除重复项。在将临时表与外部表连接时,该索引也可能在以后用于查找;如果不是,则扫描该表。有关物化的更多信息,请参阅 第 8.2.2.2 节,“使用物化优化子查询” 。 可以使用以下 optimizer_switch 系统变量标志启用或禁用这些策略中的每一个: 该 semijoin 标志控制是否使用半连接。从 MySQL 8.0.17 开始,这也适用于反连接。 如果 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 >

  • 在 MySQL 8.0.21 及更高版本中,半连接转换也可以应用于 or子查询谓词的单表 UPDATE 或 ,前提是该语句不使用 or ,并且优化器提示或通过允许半连接转换设置 DELETE [NOT] IN [NOT] EXISTS ORDER BY LIMIT optimizer_switch

     
    推荐文章