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
,并且它们比较相等。
要解决这个问题,我有两个选择:
- 而不是使用
'6'
或 '6F'
等字符串进行搜索。 . .不。您的应用程序没有理由使用错误的数据类型进行搜索。
- 如果你真的希望能够使用字符串进行搜索,你可以写
CAST(P.id AS CHAR) = '6'
[link]。但我认为这可能会对性能产生重大影响,因为我认为这可能会迫使 MySQL 扫描所有行以找到 CAST(P.id AS CHAR)
计算出正确结果的行。 (但是,我真的不确定。也许优化器足够聪明,可以弄清楚 CAST(P.id AS CHAR) = '6'
等同于 P.id = 6
而 CAST(P.id AS CHAR) = '6F'
始终为假。)
如果您想获取所有以 6 开头的 ID,请尝试这样做:
WHERE Cast(P.id as varchar(20)) LIKE '6%'
我正在使用 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
,并且它们比较相等。
要解决这个问题,我有两个选择:
- 而不是使用
'6'
或'6F'
等字符串进行搜索。 . .不。您的应用程序没有理由使用错误的数据类型进行搜索。 - 如果你真的希望能够使用字符串进行搜索,你可以写
CAST(P.id AS CHAR) = '6'
[link]。但我认为这可能会对性能产生重大影响,因为我认为这可能会迫使 MySQL 扫描所有行以找到CAST(P.id AS CHAR)
计算出正确结果的行。 (但是,我真的不确定。也许优化器足够聪明,可以弄清楚CAST(P.id AS CHAR) = '6'
等同于P.id = 6
而CAST(P.id AS CHAR) = '6F'
始终为假。)
如果您想获取所有以 6 开头的 ID,请尝试这样做:
WHERE Cast(P.id as varchar(20)) LIKE '6%'