14.3 表达式求值中的类型转换
当操作符与不同类型的操作数一起使用时,类型转换将发生,以使操作数兼容。一些转换将隐式发生。例如,MySQL 将字符串自动转换为数字,并反之。
mysql> SELECT 1+'1';
-> 2
mysql> SELECT CONCAT(2,' test');
-> '2 test'
还可以使用 CAST()
函数将数字转换为字符串。隐式转换也将发生在使用 CONCAT()
函数时,因为它期望字符串参数。
mysql> SELECT 38.8, CAST(38.8 AS CHAR);
-> 38.8, '38.8'
mysql> SELECT 38.8, CONCAT(38.8);
-> 38.8, '38.8'
请在本节后面查看隐式数字到字符串转换的字符集信息,以及适用于 CREATE TABLE ... SELECT
语句的修改规则。
以下是比较操作的转换规则:
-
如果一个或两个参数是
NULL
,比较的结果是NULL
,除非使用<=>
等值比较操作符。在NULL <=> NULL
中,结果为真。无需转换。 -
如果比较操作的两个参数都是字符串,则将它们比较为字符串。
-
如果两个参数都是整数,则将它们比较为整数。
-
十六进制值将被视为二进制字符串,如果不与数字进行比较。
-
如果一个参数是
TIMESTAMP
或DATETIME
列,并且另一个参数是常量,常量将被转换为时间戳,以便与时间戳进行比较。这是为了提高 ODBC 的兼容性。对于IN()
的参数,不执行此操作。为了安全,始终使用完整的日期、时间或时间字符串进行比较。例如,在使用BETWEEN
进行日期或时间比较时,使用CAST()
将值转换为所需的数据类型。单行子查询从表或表中获取的结果将被视为常量。例如,如果子查询返回一个整数,以便与
DATETIME
值进行比较,比较将被执行为两个整数。整数不会被转换为时间戳。要将子查询值转换为DATETIME
,使用CAST()
将子查询值转换为DATETIME
。 -
如果一个参数是十进制值,比较依赖于另一个参数。如果另一个参数是十进制或整数值,两个参数将被比较为十进制值。如果另一个参数是浮点值,两个参数将被比较为浮点值。
-
在所有其他情况下,参数将被比较为浮点数(双精度)。例如,对字符串和数字操作数的比较将发生浮点数比较。
有关将值从一个时间类型转换到另一个时间类型的信息,请见第13.2.8节,“日期和时间类型转换”。
JSON值的比较发生在两个级别上。第一个级别的比较基于比较的JSON类型。如果类型不同,比较结果由哪个类型具有更高的优先级决定。如果两个值具有相同的JSON类型,第二级别的比较将使用类型特定的规则。对于JSON和非JSON值的比较,非JSON值将被转换为JSON,并将值比较为JSON值。有关详细信息,请见JSON值的比较和排序。
以下示例illustrates将字符串转换为数字以进行比较操作:
mysql> SELECT 1 > '6x';
-> 0
mysql> SELECT 7 > '6x';
-> 1
mysql> SELECT 0 > 'x6';
-> 0
mysql> SELECT 0 = 'x6';
-> 1
在将字符串列与数字进行比较时,MySQL不能使用索引快速查找值。如果str_col
是索引字符串列,索引不能在以下语句中使用:
SELECT * FROM tbl_name WHERE str_col=1;
原因是,有许多不同的字符串可能将被转换为值1
,例如'1'
,' 1'
或'1a'
。
浮点数和大值的比较INTEGER
类型是近似的,因为整数将被转换为双精度浮点数之前比较,这不能代表所有64位整数。例如,整数值253+1不能被表示为浮点数,并将被舍入到253或253+2,取决于平台。
为了illustrate,以下比较返回true(1),但只有第一个比较返回等值:
mysql> SELECT '9223372036854775807' = 9223372036854775807;
-> 1
mysql> SELECT '9223372036854775807' = 9223372036854775806;
-> 1
在将字符串转换为浮点数和将整数转换为浮点数时,不一定会发生相同的转换。整数可能被CPU转换为浮点数,而字符串则被逐个字符转换在浮点乘法操作中。结果也可能受到计算机架构、编译器版本或优化级别等因素的影响。避免这些问题的一种方法是使用CAST()
,以便将值转换为浮点数:
mysql> SELECT CAST('9223372036854775807' AS UNSIGNED) = 9223372036854775806;
-> 0
有关浮点比较的更多信息,请见第B.3.4.8节,“浮点值的问题”。
服务器包括dtoa
,一个转换库,提供了将字符串或DECIMAL
值转换为近似值(FLOAT
或DOUBLE
)的基础:
-
跨平台的一致性转换结果,消除了例如Unix和Windows转换差异。
-
对值的精确表示,在之前的结果中没有提供足够的精度的场景中,例如在IEEE极限附近的值。
-
将数字转换为字符串格式,以尽可能高的精度。
dtoa
的精度总是与标准C库函数相同或更好。
由于这个库的转换结果在某些情况下与非dtoa
结果不同,存在应用程序之间的不兼容性。例如,依赖于特定精度结果的应用程序可能需要调整以适应额外的精度。
dtoa
库提供了以下属性。D
表示具有DECIMAL或字符串表示的值,F
表示在native binary(IEEE)格式中的浮点数。
-
F
->D
转换使用尽可能高的精度,返回D
作为最短的字符串,该字符串在被读回并四舍五入到native binary格式的最近值。 -
D
->F
转换使得F
是输入十进制字符串D
的最近native binary数。
这些属性暗示F
-> D
-> F
转换除非F
是-inf
、+inf
或NaN
,否则是无损的。后三个值不受支持,因为SQL标准将它们定义为无效的值。
对于D
-> F
-> D
转换,一个足够的条件是D
使用15个或更少的数字精度,不是denormal值、-inf
、+inf
或NaN
。在某些情况下,转换是无损的,即使D
有超过15个数字精度,但这不是总是如此。
隐式将数字或时间值转换为字符串时,产生的值的字符集和排序规则由character_set_connection
和collation_connection
系统变量确定。这些变量通常使用SET NAMES
设置。关于连接字符集的信息,请见第12.4节,“连接字符集和排序规则”。
这意味着这种转换结果是一个字符(非二进制)字符串(一个CHAR
、VARCHAR
或LONGTEXT
值),除非连接字符集设置为binary
。在这种情况下,转换结果是一个二进制字符串(一个BINARY
、VARBINARY
或LONGBLOB
值)。
对于整数表达式,前面的关于表达式evaluation的注释在assignment时有一些不同;例如,在以下语句中:
CREATE TABLE t SELECT integer_expr;
在这种情况下,表达式的结果列的类型是INT
或BIGINT
,取决于整数表达式的长度。如果整数表达式的最大长度不适合在INT
或BIGINT
中,则使用BIGINT
。长度来自SELECT
结果集元数据的max_length
值(见C API Basic Data Structures)。这意味着可以强制使用BIGINT
而不是INT
通过使用足够长的表达式:
CREATE TABLE t SELECT 000000000000000000000;