📣
TiDB Cloud Essential 开放公测中。此页面由 AI 自动翻译,英文原文请见此处。

避免隐式类型转换



本文介绍 TiDB 中隐式类型转换的规则及可能带来的后果,以及如何避免隐式类型转换。

转换规则

当 SQL 语句中谓词两边的数据类型不匹配时,TiDB 会隐式将一方或双方的数据类型转换为兼容的类型以进行谓词操作。

TiDB 中隐式类型转换的规则如下:

  • 如果其中一个或两个参数为 NULL,比较的结果为 NULL。NULL-safe <=> 等价比较运算符不需要转换,因为 NULL <=> NULL 的结果为 true
  • 如果比较的两个参数都是字符串,则作为字符串进行比较。
  • 如果两个参数都是整数,则作为整数进行比较。
  • 如果没有用数字进行比较,则十六进制值被视为二进制字符串。
  • 如果其中一个参数是十进制值,比较结果取决于另一个参数。如果另一个参数是十进制或整数值,则用十进制值进行比较;如果是浮点值,则用浮点值进行比较。
  • 如果其中一个参数是 TIMESTAMPDATETIME 列,另一个参数是常量,则在比较前将常量转换为时间戳。
  • 在所有其他情况下,参数作为浮点数(DOUBLE 类型)进行比较。

隐式类型转换引发的后果

隐式类型转换提高了人机交互的易用性,但在应用代码中应避免使用隐式类型转换,因为可能导致以下问题:

  • 索引失效
  • 精度丢失

索引失效

在以下情况下,account_id 是主键,数据类型为 varchar。在执行计划中,该 SQL 语句存在隐式类型转换,无法使用索引。

DESC SELECT * FROM `account` WHERE `account_id`=6010000000009801; +-------------------------+----------------+-----------+---------------+------------------------------------------------------------+ | id | estRows | task | access object | operator info | +-------------------------+----------------+-----------+---------------+------------------------------------------------------------+ | TableReader_7 | 8000628000.00 | root | | data:Selection_6 | | └─Selection_6 | 8000628000.00 | cop[tikv] | | eq(cast(findpt.account.account_id), 6.010000000009801e+15) | | └─TableFullScan_5 | 10000785000.00 | cop[tikv] | table:account | keep order:false | +-------------------------+----------------+-----------+---------------+------------------------------------------------------------+ 3 rows in set (0.00 sec)

简要说明运行结果:从上述执行计划可以看到,存在 Cast 操作符。

精度丢失

在以下情况下,a 字段的数据类型为 decimal(32,0)。在执行计划中发生隐式类型转换,decimal 字段和字符串常量都被转换为 double 类型。由于 double 类型的精度不如 decimal,导致精度丢失。在此情况下,SQL 语句会错误地过滤超出范围的结果集。

DESC SELECT * FROM `t1` WHERE `a` BETWEEN '12123123' AND '1111222211111111200000'; +-------------------------+---------+-----------+---------------+-------------------------------------------------------------------------------------+ | id | estRows | task | access object | operator info | +-------------------------+---------+-----------+---------------+-------------------------------------------------------------------------------------+ | TableReader_7 | 0.80 | root | | data:Selection_6 | | └─Selection_6 | 0.80 | cop[tikv] | | ge(cast(findpt.t1.a), 1.2123123e+07), le(cast(findpt.t1.a), 1.1112222111111112e+21) | | └─TableFullScan_5 | 1.00 | cop[tikv] | table:t1 | keep order:false, stats:pseudo | +-------------------------+---------+-----------+---------------+-------------------------------------------------------------------------------------+ 3 rows in set (0.00 sec)

简要说明运行结果:从上述执行计划可以看到,存在 Cast 操作符。

SELECT * FROM `t1` WHERE `a` BETWEEN '12123123' AND '1111222211111111200000'; +------------------------+ | a | +------------------------+ | 1111222211111111222211 | +------------------------+ 1 row in set (0.01 sec)

简要说明运行结果:上述执行结果为错误的。

需要帮助?

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

文档内容是否有帮助?