一个 相关子查询 是一个子查询,它包含对也出现在外部查询中的表的引用。例如
SELECT * FROM t1
WHERE column1 = ANY (SELECT column1 FROM t2
WHERE t2.column2 = t1.column2);
注意,子查询包含对 t1
的列的引用,即使子查询的 FROM
子句没有提到表 t1
。因此,MySQL 会查看子查询外部,并在外部查询中找到 t1
。
假设表 t1
包含一行,其中 column1 = 5
且 column2 = 6
;同时,表 t2
包含一行,其中 column1 = 5
且 column2 = 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 ...
是 更外层 的外部查询。
当 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
)。这反映了优化器在评估 JOIN
或 WHERE
子句时执行的基数检查,在评估任何提升的谓词之前,只有当子查询没有返回多于一行时才会执行该检查。
只要满足以下条件,就可以执行这种类型的转换
子查询可以是
SELECT
列表、WHERE
条件或HAVING
条件的一部分,但不能是JOIN
条件的一部分,也不能包含LIMIT
或OFFSET
子句。此外,子查询不能包含任何集合操作,例如UNION
。WHERE
子句可以包含一个或多个谓词,并用AND
组合。如果WHERE
子句包含OR
子句,则不能进行转换。至少一个WHERE
子句谓词必须符合转换条件,并且没有任何谓词可以拒绝转换。要符合转换条件,
WHERE
子句谓词必须是等式谓词,其中每个操作数都应该是简单的列引用。其他谓词(包括其他比较谓词)都不符合转换条件。谓词必须使用等式运算符=
来进行比较;安全空值<=>
运算符在此上下文中不受支持。仅包含内部引用的
WHERE
子句谓词不符合转换条件,因为它可以在分组之前进行评估。仅包含外部引用的WHERE
子句谓词符合转换条件,即使它可以提升到外部查询块。这是通过在派生表中添加一个没有分组的基数检查来实现的。要符合条件,
WHERE
子句谓词必须有一个操作数仅包含内部引用,另一个操作数仅包含外部引用。如果谓词因此规则而不符合条件,则会拒绝查询的转换。相关列只能出现在子查询的
WHERE
子句中(而不是在SELECT
列表、JOIN
或ORDER BY
子句、GROUP BY
列表或HAVING
子句中)。子查询的FROM
列表中的派生表中也不能包含任何相关列。相关列不能包含在聚合函数的参数列表中。
相关列必须在直接包含要考虑转换的子查询的查询块中解析。
相关列不能出现在
WHERE
子句中的嵌套标量子查询中。子查询不能包含任何窗口函数,并且不能包含任何在子查询外部的查询块中聚合的聚合函数。如果
COUNT()
聚合函数包含在子查询的SELECT
列表元素中,则它必须位于最顶层,并且不能是表达式的一部分。
另请参阅 第 15.2.15.8 节,“派生表”。