用逗号分隔的数字列表的 MySQL 奇怪行为

标签 mysql implicit-conversion

我有一个非常复杂的问题,但我把它缩小到这个,首先,让我给你一些测试数据:

运行这个:

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 解决了我原来的问题,我知道使用这种逗号分隔的列表类型结构并不是一个好主意,也就是说,它可能首先应该使用连接表来完成.不幸的是,我在一个非常大的系统中工作,此时进行更改是不切实际的。

我只是对为什么会发生这种特定行为感兴趣。

最佳答案

您获得第三行的原因是 MySQL 执行的隐式数据类型转换。您的查询在 WHERE 子句中有谓词(条件)

  WHERE value = 1

在相等比较运算符(等号)的右侧,我们有一个数字文字。在左侧,我们有一个数据类型为 TEXT 的列。

MySQL 不可能对这两种不同的数据类型进行比较。

因此,MySQL 将一侧或另一侧转换为兼容的类型,因此可以执行比较。在这种情况下,MySQL 将列中的值转换为数字,因此它与数字 文字进行比较。

作为演示,我们可以添加一个零(强制 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 停止的地方。因此转换返回数值 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'

关于用逗号分隔的数字列表的 MySQL 奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39520783/

相关文章:

mysql - 无法弄清楚如何使用中间 id 表组合两个表

mysql - 如何使这个删除查询在 MySQL 中更有效?

c# - 动物 thisIsACat = new Cat(); - 这是隐式转换吗?

c# - 为什么 C# 中的这种隐式类型转换会失败?

MySQL:如何将多个查询组合成一个使用具有不同条件的同一张表的查询?

java - Hibernate 内连接 ManyToOne

mysql - 使用 View 进行递归 cte

scala - 为什么Option不直接扩展Iterable特性?

c - C中赋值期间的隐式转换?

scala - 关于隐式的奇怪错误消息