【问题标题】:Unexpected result in WHERE clause on AI ID fieldAI ID 字段的 WHERE 子句中出现意外结果
【发布时间】:2021-03-18 08:10:32
【问题描述】:

我的 MySQL 数据库中有一个名为 users 的表,并且多年来我一直在将此数据库与具有 ORM 结构的 Ruby on Rails 应用程序一起使用。该表有id字段,该字段配置为AI(自增),BIGINT。

我的用户表示例;

+----+---------+
| id | name    |
+----+---------+
|  1 | John    |
|  2 | Tommy   |
|  3 | ...     |
|  4 | ...     |
|  5 | ...     |
|  6 | ...     |
+----+---------+

我面临的问题是当我执行以下查询时,我得到了意外的行。

SELECT * FROM users WHERE id = '1AW3F4SEFR';

此查询返回与以下查询完全相同的值,

 SELECT * FROM users WHERE id = 1;

我不知道为什么 SQL 允许我在数据类型 INT 的 WHERE 子句中使用字符串。正如我们从示例中看到的那样,我的数据库将我给出的字符串转换为位置 0 处的整数。我的意思是,我搜索 1AW3F4SEFR 并且我希望不会得到任何结果。但是 SQL 语句返回 id = 1 的结果。

在 Oracle SQL 中,这个完全相同的查询的行为是完全不同的。所以,我相信 MySQL 有一些不同的地方。但我不确定是什么原因造成的。

【问题讨论】:

  • 当您将字符串与数字进行比较时,它会在比较之前自动将字符串转换为数字。
  • 如果字符串以数字开头,将其转换为数字会返回该初始数字。
  • 所以'123abc' 变成了123
  • ...如果您使用的是 MySQL。其他数据库引发错误,或将整数转换为字符串。
  • 我一直认为这是 MySQL 中的一个缺陷。但是在我看来,无论如何你都不应该将数值与字符串进行比较。如果列是数字,则将其与数字进行比较。不,如果你将数字与字符串进行比较,MySQL 会应用这种奇怪的转换。 AFAIK 没有办法防止这种情况发生。

标签: mysql sql orm


【解决方案1】:

正如请求 cmets 中所解释的,MySQL 有一种将字符串转换为数字的奇怪方式。它只是从左边获取与数字一样多的字符串,并忽略其余部分。如果字符串不以数字开头,则转换默认为 0。

示例:'123' => 123、'12.3' => 12.3、'.123' => 0.123、'12A3' => 12、'A123' => 0、'.1A1.' => 0.1

演示:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=55cd18865fad4738d03bf28082217ca8

MySQL 不会像其他 DBMS 那样在这里引发错误,这很容易导致长时间未被检测到的不需要的查询结果。

解决方案很简单:不要让这种情况发生。不要将数字列与字符串进行比较。如果在某些应用程序中输入了 ID '1AW3F4SEFR',则在应用程序中引发错误,甚至阻止输入此值。运行 SQL 查询时,请确保传递一个数值,因此“1AW3F4SEFR”甚至无法进入 DBMS。 (查看如何使用准备好的语句并将不同类型的参数传递给您的编程语言中的数据库系统。)

如果由于某种原因您想为 ID 传递一个字符串(但我想不出任何这样的原因)并且希望通过在类似“1AW3F4SEFR”的 ID 的情况下不返回任何行来使您的查询安全,检查ID字符串是否代表查询中的整数值。您可以为此使用REGEXP

SELECT * FROM users WHERE id = @id AND @id REGEXP '^[0-9]+$';

因此,您只考虑整数 ID 字符串,仍然允许 DBMS 在查找 ID 时使用索引。

演示:https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=56f8ee902342752933c20b8762f14dbb

【讨论】:

  • 非常感谢您提供这个解释性的答案,我有一个表格代表我的应用程序中的属性值。有些值是那些属性的ID,有些不是,所以我基本上检查了SQL表是否存在这个特定的行。如果没有,我想显示 ID 本身。例如,您正在输入用户的性别属性,但用户的名称也存储在同一张表中(出于某种原因,我需要这样做。)。我查询每个属性,如果找不到任何结果,我会显示值本身(值本身是手动输入的数据)。希望你理解
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-11
  • 2021-08-29
  • 1970-01-01
  • 2014-04-10
  • 2022-01-03
相关资源
最近更新 更多