文档主页
MySQL 8.4 参考手册
相关文档 下载本手册
PDF (US Ltr) - 39.9MB
PDF (A4) - 40.0MB
手册页 (TGZ) - 258.5KB
手册页 (Zip) - 365.5KB
信息 (Gzip) - 4.0MB
信息 (Zip) - 4.0MB


MySQL 8.4 参考手册  /  ...  /  相关子查询

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) 将为 TRUE,但在本例中,子查询中的 WHERE 子句为 FALSE(因为 (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 ... 是一个更靠外的外部查询。

optimizer_switch 变量的 subquery_to_derived 标志启用时,优化器可以将关联的标量子查询转换为派生表。考虑此处显示的查询

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 子句谓词必须有一个操作数仅包含内部引用,而另一个操作数仅包含外部引用。如果谓词由于此规则而不符合条件,则拒绝转换查询。

  • 关联列只能出现在子查询的 WHERE 子句中(而不能出现在 SELECT 列表、JOINORDER BY 子句、GROUP BY 列表或 HAVING 子句中)。子查询的 FROM 列表中的派生表中也不能有任何关联列。

  • 关联列不能包含在聚合函数的参数列表中。

  • 关联列必须在直接包含要转换的子查询的查询块中解析。

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

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

另请参见 第 15.2.15.8 节“派生表”