常量折叠优化:修订间差异

来自泡泡学习笔记
跳到导航 跳到搜索
无编辑摘要
无编辑摘要
 
第86行: 第86行:


*限制。以下情况下无法使用此优化:
*限制。以下情况下无法使用此优化:
**使用BETWEEN或IN进行比较的情况。
**使用BETWEEN或IN进行比较的情况。
**使用BIT列或使用日期或时间类型的列的情况。
**使用BIT列或使用日期或时间类型的列的情况。
**在准备阶段的预处理语句中,尽管在实际执行预处理语句时可以在优化阶段应用此优化。这是因为在语句准备阶段,常量的值尚未确定。
**在准备阶段的预处理语句中,尽管在实际执行预处理语句时可以在优化阶段应用此优化。这是因为在语句准备阶段,常量的值尚未确定。

2023年7月19日 (三) 08:09的最新版本

在查询优化过程中,将那些常量和列值之间的比较,其中常量值超出范围或与列类型不匹配的情况,不再逐行处理,而是一次性处理。可以采用这种方式处理的比较运算符有 >、>=、<、<=、<>/!=、= 和<=>。


考虑以下语句创建的表:

CREATE TABLE t (c TINYINT UNSIGNED NOT NULL);


在查询 SELECT * FROM t WHERE c < 256 的 WHERE 条件中,包含了超出 TINYINT UNSIGNED 列范围的整数常量 256。先前的处理方式是将两个操作数都视为较大的类型,但现在,由于 c 的任何允许值都小于常量值,因此 WHERE 表达式可以简化为 WHERE 1,这样查询可以重写为 SELECT * FROM t WHERE 1。


这使得优化器可以完全移除 WHERE 表达式。如果列 c 是可空的(即仅定义为 TINYINT UNSIGNED),则查询将被重写为:

SELECT * FROM t WHERE ti IS NOT NULL


折叠是针对与支持的MySQL列类型进行常量比较的,具体如下所示:

  • 整数列类型。整数类型与以下类型的常量进行比较,如下所述:

1、整数值。如果常量超出列类型的范围,比较将被简化为1或IS NOT NULL,如之前所示。


如果常量是范围边界,则比较将被简化为=。例如(使用已定义的相同表):

mysql> EXPLAIN SELECT * FROM t WHERE c >= 255;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: t
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 5
     filtered: 20.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec) 

mysql> SHOW WARNINGS;
*************************** 1. row ***************************
  Level: Note
   Code: 1003
Message: /* select#1 */ select `test`.`t`.`ti` AS `ti` from `test`.`t` where (`test`.`t`.`ti` = 255)
1 row in set (0.00 sec)


2、浮点数或定点数值。如果常量是十进制类型(如DECIMAL、REAL、DOUBLE或FLOAT),并且具有非零的小数部分,则它不能相等;相应地进行简化。对于其他比较,根据符号四舍五入为整数值,然后执行范围检查,并按照整数-整数比较的方式处理。


无法表示为DECIMAL的REAL值会四舍五入为.01或-.01,然后按照DECIMAL处理。


3、字符串类型。尝试将字符串值解释为整数类型,然后按照整数值之间的比较进行处理。如果失败,则尝试将该值处理为REAL类型。


  • DECIMAL或REAL列。根据以下规则比较十进制类型的常量:

1、整数值。对列值的整数部分进行范围检查。如果无法简化,则将常量转换为与列值具有相同小数位数的DECIMAL类型,然后将其作为DECIMAL进行检查(见下文)。


2、DECIMAL或REAL值。检查是否溢出(即常量的整数部分是否比列的小数类型允许的位数多)。如果是,则进行简化。


如果常量的小数位数比列的类型更多,则截断常量。如果比较运算符是=或< >,则进行简化。如果运算符是>=或<=,则由于截断而调整运算符。例如,如果列的类型是DECIMAL(3,1),SELECT * FROM t WHERE f >= 10.13将变为SELECT * FROM t WHERE f > 10.1。


如果常量的小数位数少于列的类型,则将其转换为具有相同位数的常量。对于REAL值的下溢(即表示不了足够的小数位数),将常量转换为0的DECIMAL类型。


3、字符串值。如果该值可以解释为整数类型,则按照该类型处理。否则,尝试将其作为REAL类型处理。


  • FLOAT或DOUBLE列。将与常量进行比较的FLOAT(m,n)或DOUBLE(m,n)值的处理方式如下:


如果值超出了列的范围,进行简化。


如果值的小数位数超过n,进行截断,并在简化过程中进行补偿。对于=和<>的比较,根据之前描述的方式简化为TRUE、FALSE或IS [NOT] NULL;对于其他操作符,调整操作符。


如果值的整数位数超过了m,进行简化。


  • 限制。以下情况下无法使用此优化:
    • 使用BETWEEN或IN进行比较的情况。
    • 使用BIT列或使用日期或时间类型的列的情况。
    • 在准备阶段的预处理语句中,尽管在实际执行预处理语句时可以在优化阶段应用此优化。这是因为在语句准备阶段,常量的值尚未确定。