mysql 中数字的隐式转换

Implicit conversion of a numeric in mysql

table 结构:

CREATE TABLE `test_table` (
  `pk_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL,
  `password` varchar(128) NOT NULL,
  `version` bigint(20) NOT NULL,
  PRIMARY KEY (`pk_id`)
) ENGINE=InnoDB AUTO_INCREMENT=18446744073709551601 DEFAULT CHARSET=utf8;

数据:

/*
-- Query: SELECT * FROM test_table
-- Date: 2017-09-15 16:51
*/
INSERT INTO `test_table` (`pk_id`,`name`,`password`,`version`) VALUES (10545342555594296735,'testabc','testabc',5);
INSERT INTO `test_table` (`pk_id`,`name`,`password`,`version`) VALUES (10545342555594297155,'testdef','testdef',0);
INSERT INTO `test_table` (`pk_id`,`name`,`password`,`version`) VALUES (10545342555594298139,'testghi','testghi',0);
INSERT INTO `test_table` (`pk_id`,`name`,`password`,`version`) VALUES (18446744073709551601,'testjkl','testjkl',0);
INSERT INTO `test_table` (`pk_id`,`name`,`password`,`version`) VALUES (18446744073709551602,'testmno','testmno',0);
INSERT INTO `test_table` (`pk_id`,`name`,`password`,`version`) VALUES (18446744073709551603,'testpqr','testpqr',0);

运行 sql:

SELECT 
    *
FROM
    test_table
WHERE
    CAST(pk_id AS CHAR) = 18446744073709551601;

结果:

# pk_id, name, password, version
'18446744073709551601', 'testjkl', 'testjkl', '0'
'18446744073709551602', 'testmno', 'testmno', '0'
'18446744073709551603', 'testpqr', 'testpqr', '0'

我对隐式转换感到困惑,发生了什么?我已经查看了official website关于隐式转换rules.But我仍然没有understand.Someone可以帮助我。

您可以查看此 link 以获取有关类型转换的更多信息。

The following rules describe how conversion occurs for comparison operations:

1 - If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.

2 - If both arguments in a comparison operation are strings, they are compared as strings.

3 - If both arguments are integers, they are compared as integers.

4 - Hexadecimal values are treated as binary strings if not compared to a number.

5 - If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. Note that this is not done for the arguments to IN()! To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.

6 - A single-row subquery from a table or tables is not considered a constant. For example, if a subquery returns an integer to be compared to a DATETIME value, the comparison is done as two integers. The integer is not converted to a temporal value. To compare the operands as DATETIME values, use CAST() to explicitly convert the subquery value to DATETIME.

7 - If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.

8 - In all other cases, the arguments are compared as floating-point (real) numbers.

在您的例子中,参数是作为浮点(实数)数进行比较的。使用浮点数(或转换为浮点数的值)的比较是近似值,因为此类数字不精确。这可能会导致结果看起来不一致。

您可以检查以下查询是否会为 VALUE1、VALUE2、VALUE3 和 VALUE4 生成相同的结果。这就是为什么您的查询会返回这样的结果。

SELECT 
    '18446744073709551601' + 0E0 AS VALUE1,
    '18446744073709551602' + 0E0 AS VALUE2,
    '18446744073709551603' + 0E0 AS VALUE3,
     18446744073709551601 + 0E0 AS VALUE4

如果您希望查询按预期运行,您应该将两个参数都更改为字符串

CAST(pk_id AS CHAR) = '18446744073709551601';