Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 Reference Manual  /  ...  /  Correlated Subqueries

15.2.15.7 相关子查询

相关子查询是一种包含对外部查询中表的引用子的查询。例如:

SELECT * FROM t1
  WHERE column1 = ANY (SELECT column1 FROM t2
                       WHERE t2.column2 = t1.column2);

注意,子查询中包含了t1的一列,即使子查询的FROM子句没有提到t1表。因此,MySQL会从外部查询中查找t1

假设表t1包含一行,其中column1 = 5column2 = 6;而表t2包含一行,其中column1 = 5column2 = 7。简单的表达式... WHERE column1 = ANY (SELECT column1 FROM t2)将是TRUEFALSE(因为(5,6)不等于(5,7)),因此整个表达式为FALSE

作用域规则:MySQL从内到外评估。例如:

SELECT column1 FROM t1 AS x
  WHERE x.column1 = (SELECT column1 FROM t2 AS x
    WHERE x.column1 = (SELECT column1 FROM t3
      WHERE x.column2 = t3.column1));

在这个语句中,x.column2必须是表t2中的列,因为SELECT column1 FROM t2 AS x ...t2重命名。它不是表t1中的列,因为SELECT column1 FROM t1 ...是外部查询,更远

优化器可以将相关的标量子查询转换为派生表,当subquery_to_derived optimizer_switch 变量启用时考虑以下查询:

SELECT * FROM t1 
    WHERE ( SELECT a FROM t2 
              WHERE t2.a=t1.a ) > 0;

为了避免对给定派生表多次物化,我们可以将派生表添加一个组合,来自内嵌查询中的表(t2.a)的连接列,然后在外部连接上提升谓词(t1.a = derived.a),以选择正确的组合与外部行匹配。 (如果子查询已经有了明确的组合,额外的组合将添加到组合列表末尾。) 上述查询可以被重写为:

SELECT t1.* FROM t1 
    LEFT OUTER JOIN
        (SELECT a, COUNT(*) AS ct FROM t2 GROUP BY a) AS derived
    ON  t1.a = derived.a 
        AND 
        REJECT_IF(
            (ct > 1),
            "ERROR 1242 (21000): Subquery returns more than 1 row"
            )
    WHERE derived.a > 0;

在重写的查询中,REJECT_IF() 表示一个内部函数,它测试给定的条件(这里是比较 ct > 1),并且如果条件为真,则引发给定的错误(在这个情况下是ER_SUBQUERY_NO_1_ROW)。这反映了优化器在评估 JOINWHERE 子句时执行的卡内度检查,之前没有任何提升谓词,只有当子查询不返回多行时才进行。

这种转换可以执行,提供以下条件满足:

  • 子查询可以是SELECT 列表、WHERE 条件或HAVING 条件,但不能是JOIN 条件,也不能包含LIMITOFFSET 子句。此外,子查询不能包含任何集合操作,如UNION

  • WHERE 子句可能包含一个或多个谓词,使用AND 组合。如果WHERE 子句包含OR 子句,那么它不能被转换。其中至少一个WHERE 子句谓词必须是可转换的,且没有任何之一可以拒绝转换。

  • 要使WHERE 子句谓词可转换,它必须是一个等值谓词,其中每个操作数应该是一个简单列引用。其他谓词——包括其他比较谓词——不具备转换资格。谓词必须使用= 运算符进行比较;在这个上下文中,不支持安全等值运算符≪=>

  • 只有包含内嵌引用且不能被评估在分组前面的WHERE子句谓词不适合转换,其他情况都可以转换。即使可以提升到外层查询块,也可以转换,这是通过在衍生表中添加卡度检查而可能的。

  • 一个WHERE子句谓词必须有一个操作数包含内嵌引用,另一个操作数包含外嵌引用。如果谓词不符合这个规则,查询转换将被拒绝。

  • 一个相关列只能出现在子查询的WHERE子句中(而不是在SELECT列表、JOINORDER BY子句、GROUP BY列表或HAVING子句中)。衍生表中的子查询也不能包含相关列。

  • 一个相关列不能作为聚合函数的参数之一。

  • 相关列必须在考虑转换的子查询所在的查询块中直接解析。

  • 相关列不能出现在WHERE子句中的嵌套标量子查询中。

  • 子查询不能包含窗口函数,并且不能包含外层查询块的聚合函数。子查询中的COUNT()聚合函数,如果在SELECT列表中,必须是最顶层的,并且不能是表达式的一部分。

还见第15.2.15.8节,“派生表”