MySQL 中的相等真的意味着相等吗?

Does equal really means equal in MySQL?

我正在使用 MySQL 作为 Linux 服务器中的后端数据库学习 Node JS,我遇到了一些我不理解的东西,它都与 MySQL 本身有关。

我有以下 table 一行数据。 这一行的id是6

CREATE TABLE `posts` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `authorID` int(11) DEFAULT NULL,
      `title` varchar(90) COLLATE utf8_bin DEFAULT NULL,
      `body` mediumtext COLLATE utf8_bin,
      `createdDate` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

因此以下查询工作正常并且 return 是 table 中的一行。

 SELECT P.id,U.userName as `authorName`,U.userEmail as `authorEmail`,P.title,P.body,P.createdDate 
FROM posts P LEFt JOIN users U ON P.authorID = U.id WHERE P.id = '6'

这个其他查询也有效(return没有数据):

    SELECT P.id,U.userName as `authorName`,U.userEmail as `authorEmail`,P.title,P.body,P.createdDate 
FROM posts P LEFt JOIN users U ON P.authorID = U.id WHERE P.id = '60'
 

但是……!以下查询是我迷路的地方(它 return ID 为 6 的行!!!):

    SELECT P.id,U.userName as `authorName`,U.userEmail as `authorEmail`,P.title,P.body,P.createdDate 
FROM posts P LEFt JOIN users U ON P.authorID = U.id WHERE P.id = '6F'

为什么?我在单引号内传递了文字 6F(以确保),它不应该 return 任何东西。没有 ID 为 6F 的 post。顺便说一句,我可以用 6whatever 替换 6F,问题仍然存在。然而,F6 不会 return 预期的数据。

这是为什么?是不是因为 F 是一个字符并且 MySQL 需要一个整数,因此截断了不属于整数的任何内容?

更重要的是,我怎样才能消除这种歧义?

您的假设是正确的:MySQL 会将以整数开头的字符串视为整数,并从您的字符串中解析出任何 'Char' 值。如果您的列是 Integer 类型,那么它将尝试转换您的字符串文字。

为了解决这个问题,可以使用CAST函数来确保没有歧义:

SELECT * FROM user WHERE CAST(user.id AS CHAR(9)) = '1F';

但是严格来说,你真的不应该让你的系统将 1F 传递到 ID 字段,因为你的 ID 字段应该是一个整数(理解用户错误或输入应该被处理在进入查询之前)

the MySQL 8.0 Reference Manual, §12.2 "Type Conversion in Expression Evaluation" 中所述:

When an operator is used with operands of different types, type conversion occurs to make the operands compatible. Some conversions occur implicitly. […]

和:

The following rules describe how conversion occurs for comparison operations:

  • […]
  • In all other cases, the arguments are compared as floating-point (real) numbers. For example, a comparison of string and numeric operands takes place as a comparison of floating-point numbers.

所以你的整数 6 和字符串 '6F' 都被转换为实数 6.0,并且它们比较相等。

要解决这个问题,我有两个选择:

  1. 而不是使用 '6''6F' 等字符串进行搜索。 . .不。您的应用程序没有理由使用错误的数据类型进行搜索。
  2. 如果你真的希望能够使用字符串进行搜索,你可以写CAST(P.id AS CHAR) = '6' [link]。但我认为这可能会对性能产生重大影响,因为我认为这可能会迫使 MySQL 扫描所有行以找到 CAST(P.id AS CHAR) 计算出正确结果的行。 (但是,我真的不确定。也许优化器足够聪明,可以弄清楚 CAST(P.id AS CHAR) = '6' 等同于 P.id = 6CAST(P.id AS CHAR) = '6F' 始终为假。)

如果您想获取所有以 6 开头的 ID,请尝试这样做:

WHERE Cast(P.id as varchar(20)) LIKE '6%'