MySQL 用逗号分隔的数字列表的奇怪行为
MySQL strange behavior with comma separated list of numbers
我有一个非常复杂的问题,但我把它缩小到这个,首先,让我给你一些测试数据:
运行这个:
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`value` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
INSERT INTO test (value) VALUES
(1),
('1'),
('1,2'),
('3');
现在运行这个查询:
SELECT * FROM test WHERE value = 1;
在这种情况下,我希望只获得前两行,其中值输入为数字 1 或“1”字符,但出于某种原因,这就是我得到的:
1, 1
2, 1
3, 1,2
我的问题是,为什么我得到第三行?
注意:这是我的mysql版本:5.6.28-0ubuntu0.14.04.1
此外,我已经使用 FIND_IN_SET 解决了我原来的问题,我知道使用逗号分隔的列表类型结构不是一个好主意,即,它可能应该用首先加入 table。不幸的是,我在一个非常大的系统中工作,此时进行更改是不切实际的。
我只是想知道为什么会发生这种特定行为。
您获得第三行的原因是 隐式数据类型转换 由 MySQL 执行。您的查询在 WHERE 子句
中有一个谓词(条件)
WHERE value = 1
在相等比较运算符(等号)的右侧,我们有一个数字文字。在左侧,我们有一个数据类型为 TEXT 的列。
MySQL 无法比较这两种不同的数据类型。
因此,MySQL 将一侧或另一侧转换为兼容的类型,因此可以执行比较。在这种情况下,MySQL 将列中的值转换为 numeric,因此它与 numeric 文字进行比较。
作为演示,我们可以添加一个零(强制 MySQL 进行转换),并在 SELECT.
中展示结果
SELECT t.value, t.value + 0 FROM test t
t.value t.value + 0
------- -----------
1 1
1 1
1,2 1
3 3
MySQL 参考手册中的某处记录了 MySQL 如何进行转换。冒着误解手册内容的风险:MySQL 从左到右逐个字符地读取字符串,直到遇到无法再转换为数字的字符。
在字符串 '1,2'
的情况下,它恰好是逗号字符。这就是 MySQL 停止的地方。因此转换 returns 数值为 1。您指出其他数据库在尝试将该字符串转换为数字时会抛出错误是正确的。但是 MySQL 不会抛出错误或警告。
参考:Type Conversion in Expression Evaluation http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html
基本上,查询中的谓词等同于指定:
WHERE value + 0 = 1
强制将列 value
的内容转换为数字,然后与数字文字进行比较。
这就是返回第三行的原因。
要获得不同的结果,请考虑与 字符串文字
进行比较
WHERE value = '1'
我有一个非常复杂的问题,但我把它缩小到这个,首先,让我给你一些测试数据:
运行这个:
CREATE TABLE `test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`value` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
INSERT INTO test (value) VALUES
(1),
('1'),
('1,2'),
('3');
现在运行这个查询:
SELECT * FROM test WHERE value = 1;
在这种情况下,我希望只获得前两行,其中值输入为数字 1 或“1”字符,但出于某种原因,这就是我得到的:
1, 1
2, 1
3, 1,2
我的问题是,为什么我得到第三行?
注意:这是我的mysql版本:5.6.28-0ubuntu0.14.04.1
此外,我已经使用 FIND_IN_SET 解决了我原来的问题,我知道使用逗号分隔的列表类型结构不是一个好主意,即,它可能应该用首先加入 table。不幸的是,我在一个非常大的系统中工作,此时进行更改是不切实际的。
我只是想知道为什么会发生这种特定行为。
您获得第三行的原因是 隐式数据类型转换 由 MySQL 执行。您的查询在 WHERE 子句
中有一个谓词(条件) WHERE value = 1
在相等比较运算符(等号)的右侧,我们有一个数字文字。在左侧,我们有一个数据类型为 TEXT 的列。
MySQL 无法比较这两种不同的数据类型。
因此,MySQL 将一侧或另一侧转换为兼容的类型,因此可以执行比较。在这种情况下,MySQL 将列中的值转换为 numeric,因此它与 numeric 文字进行比较。
作为演示,我们可以添加一个零(强制 MySQL 进行转换),并在 SELECT.
中展示结果 SELECT t.value, t.value + 0 FROM test t
t.value t.value + 0
------- -----------
1 1
1 1
1,2 1
3 3
MySQL 参考手册中的某处记录了 MySQL 如何进行转换。冒着误解手册内容的风险:MySQL 从左到右逐个字符地读取字符串,直到遇到无法再转换为数字的字符。
在字符串 '1,2'
的情况下,它恰好是逗号字符。这就是 MySQL 停止的地方。因此转换 returns 数值为 1。您指出其他数据库在尝试将该字符串转换为数字时会抛出错误是正确的。但是 MySQL 不会抛出错误或警告。
参考:Type Conversion in Expression Evaluation http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html
基本上,查询中的谓词等同于指定:
WHERE value + 0 = 1
强制将列 value
的内容转换为数字,然后与数字文字进行比较。
这就是返回第三行的原因。
要获得不同的结果,请考虑与 字符串文字
进行比较 WHERE value = '1'