📣

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

Eliminate Max/Min

当一个 SQL 语句包含 max/min 函数时,查询优化器会尝试通过应用 max/min 优化规则,将这些聚合函数转换为 TopN 操作符。这样,TiDB 就能通过索引更高效地执行查询。

此优化规则根据 select 语句中 max/min 函数的数量,分为以下两类:

仅含一个 max/min 函数

当一个 SQL 语句满足以下条件时,应用此规则:

  • 语句中仅包含一个聚合函数,为 maxmin
  • 该聚合函数没有相关的 group by 子句。

例如:

select max(a) from t

优化规则会将该语句重写为:

select max(a) from (select a from t where a is not null order by a desc limit 1) t

当列 a 有索引,或者列 a 是某个复合索引的前缀时,借助索引,新 SQL 语句只需扫描一行数据即可找到最大值或最小值。这避免了全表扫描。

该示例语句的执行计划如下:

mysql> explain select max(a) from t; +------------------------------+---------+-----------+-------------------------+-------------------------------------+ | id | estRows | task | access object | operator info | +------------------------------+---------+-----------+-------------------------+-------------------------------------+ | StreamAgg_13 | 1.00 | root | | funcs:max(test.t.a)->Column#4 | | └─Limit_17 | 1.00 | root | | offset:0, count:1 | | └─IndexReader_27 | 1.00 | root | | index:Limit_26 | | └─Limit_26 | 1.00 | cop[tikv] | | offset:0, count:1 | | └─IndexFullScan_25 | 1.00 | cop[tikv] | table:t, index:idx_a(a) | keep order:true, desc, stats:pseudo | +------------------------------+---------+-----------+-------------------------+-------------------------------------+

含多个 max/min 函数

当一个 SQL 语句满足以下条件时,应用此规则:

  • 语句中包含多个聚合函数,且全部为 maxmin
  • 这些聚合函数都没有相关的 group by 子句。
  • 每个 max/min 函数中的列都具有索引以保持顺序。

例如:

select max(a) - min(a) from t

优化规则首先会检查列 a 是否有索引以保持其顺序。如果有,SQL 语句会被重写为两个子查询的笛卡尔积:

select max_a - min_a from (select max(a) as max_a from t) t1, (select min(a) as min_a from t) t2

通过重写,优化器可以对两个子查询分别应用仅含一个 max/min 函数的规则。语句会被重写为:

select max_a - min_a from (select max(a) as max_a from (select a from t where a is not null order by a desc limit 1) t) t1, (select min(a) as min_a from (select a from t where a is not null order by a asc limit 1) t) t2

同样地,如果列 a 有索引以保持其顺序,优化后只需扫描两行数据,而不是全表扫描。然而,如果列 a 没有索引以保持其顺序,此规则会导致两次全表扫描,但如果不重写,执行只需一次全表扫描。因此,在这种情况下,不应用此规则。

最终的执行计划如下:

mysql> explain select max(a)-min(a) from t; +------------------------------------+---------+-----------+-------------------------+-------------------------------------+ | id | estRows | task | access object | operator info | +------------------------------------+---------+-----------+-------------------------+-------------------------------------+ | Projection_17 | 1.00 | root | | minus(Column#4, Column#5)->Column#6 | | └─HashJoin_18 | 1.00 | root | | CARTESIAN inner join | | ├─StreamAgg_45(Build) | 1.00 | root | | funcs:min(test.t.a)->Column#5 | | │ └─Limit_49 | 1.00 | root | | offset:0, count:1 | | │ └─IndexReader_59 | 1.00 | root | | index:Limit_58 | | │ └─Limit_58 | 1.00 | cop[tikv] | | offset:0, count:1 | | │ └─IndexFullScan_57 | 1.00 | cop[tikv] | table:t, index:idx_a(a) | keep order:true, stats:pseudo | | └─StreamAgg_24(Probe) | 1.00 | root | | funcs:max(test.t.a)->Column#4 | | └─Limit_28 | 1.00 | root | | offset:0, count:1 | | └─IndexReader_38 | 1.00 | root | | index:Limit_37 | | └─Limit_37 | 1.00 | cop[tikv] | | offset:0, count:1 | | └─IndexFullScan_36 | 1.00 | cop[tikv] | table:t, index:idx_a(a) | keep order:true, desc, stats:pseudo | +------------------------------------+---------+-----------+-------------------------+-------------------------------------+

文档内容是否有帮助?