📣

TiDB Cloud Serverless 现已更名为
Starter
!此页面由 AI 自动翻译,英文原文请见
此处。

Subquery

本文介绍 TiDB 中的子查询语句及其分类。

概述

子查询是嵌套在另一个 SQL 查询中的查询。通过子查询,查询结果可以在另一个查询中使用。

以下以 Bookshop 应用为例,介绍子查询。

子查询语句

在大多数情况下,子查询主要有五种类型:

  • 标量子查询,例如 SELECT (SELECT s1 FROM t2) FROM t1
  • 派生表,例如 SELECT t1.s1 FROM (SELECT s1 FROM t2) t1
  • 存在性测试,例如 WHERE NOT EXISTS(SELECT ... FROM t2)WHERE t1.a IN (SELECT ... FROM t2)
  • 数量比较,例如 WHERE t1.a = ANY(SELECT ... FROM t2)WHERE t1.a = ALL(SELECT ... FROM t2)
  • 作为比较操作符操作数的子查询,例如 WHERE t1.a > (SELECT ... FROM t2)

子查询的分类

子查询可以分为 Related Subquery 和 Self-contained Subquery。TiDB 对这两类子查询的处理方式不同。

子查询是否相关,取决于它是否引用了外层查询中的列。

Self-contained 子查询

对于作为比较操作符(>, >=, <, <=, =!=)操作数的自包含子查询,内部子查询只会执行一次,TiDB 会在执行计划阶段将其重写为常量。

例如,查询 authors 表中年龄大于平均年龄的作者,可以使用子查询作为比较操作符的操作数。

SELECT * FROM authors a1 WHERE (IFNULL(a1.death_year, YEAR(NOW())) - a1.birth_year) > ( SELECT AVG(IFNULL(a2.death_year, YEAR(NOW())) - a2.birth_year) AS average_age FROM authors a2 )

在 TiDB 执行上述查询之前,先执行内部子查询:

SELECT AVG(IFNULL(a2.death_year, YEAR(NOW())) - a2.birth_year) AS average_age FROM authors a2;

假设结果为 34,即平均年龄为 34,TiDB 会将其作为常量替换原始子查询。

SELECT * FROM authors a1 WHERE (IFNULL(a1.death_year, YEAR(NOW())) - a1.birth_year) > 34;

结果示例如下:

+--------+-------------------+--------+------------+------------+ | id | name | gender | birth_year | death_year | +--------+-------------------+--------+------------+------------+ | 13514 | Kennith Kautzer | 1 | 1956 | 2018 | | 13748 | Dillon Langosh | 1 | 1985 | NULL | | 99184 | Giovanny Emmerich | 1 | 1954 | 2012 | | 180191 | Myrtie Robel | 1 | 1958 | 2009 | | 200969 | Iva Renner | 0 | 1977 | NULL | | 209671 | Abraham Ortiz | 0 | 1943 | 2016 | | 229908 | Wellington Wiza | 1 | 1932 | 1969 | | 306642 | Markus Crona | 0 | 1969 | NULL | | 317018 | Ellis McCullough | 0 | 1969 | 2014 | | 322369 | Mozelle Hand | 0 | 1942 | 1977 | | 325946 | Elta Flatley | 0 | 1933 | 1986 | | 361692 | Otho Langosh | 1 | 1931 | 1997 | | 421294 | Karelle VonRueden | 0 | 1977 | NULL | ...

对于存在性测试(Existential Test)和数量比较(Quantified Comparison)等自包含子查询,TiDB 会对其进行重写,转化为等价的查询以提升性能。更多信息请参见 Subquery Related Optimizations

对于 Related 子查询,由于内部子查询引用了外层查询的列,每次外层查询的每一行,子查询都需要执行一次。这意味着假设外层查询返回 1000 万条结果,子查询也会执行 1000 万次,消耗更多时间和资源。

因此,在处理过程中,TiDB 会尝试进行 Decorrelate of Correlated Subquery,在执行计划层面优化查询效率。

以下语句用于查询年龄大于同一性别其他作者平均年龄的作者:

SELECT * FROM authors a1 WHERE (IFNULL(a1.death_year, YEAR(NOW())) - a1.birth_year) > ( SELECT AVG( IFNULL(a2.death_year, YEAR(NOW())) - IFNULL(a2.birth_year, YEAR(NOW())) ) AS average_age FROM authors a2 WHERE a1.gender = a2.gender );

TiDB 会将其重写为等价的 join 查询:

SELECT * FROM authors a1, ( SELECT gender, AVG( IFNULL(a2.death_year, YEAR(NOW())) - IFNULL(a2.birth_year, YEAR(NOW())) ) AS average_age FROM authors a2 GROUP BY gender ) a2 WHERE a1.gender = a2.gender AND (IFNULL(a1.death_year, YEAR(NOW())) - a1.birth_year) > a2.average_age;

作为最佳实践,在实际开发中,建议避免通过相关子查询进行查询,如果可以用性能更优的等价查询实现。

阅读更多

需要帮助?

DiscordSlack 上向社区提问,或 提交支持工单

文档内容是否有帮助?