MySQL 偶尔会返回错误的值


MySQL Occasionally Returns Wrong Value

这是一个一般性的问题,我已经挠头了一段时间了。 我公司的数据库每天处理大约 2k 行。 99.9% 的时间,我们对设置的不同 SELECT 语句中返回的值没有问题。 但是,在少数情况下,我们的数据库将"出现故障"并返回与请求完全不同的行的值。

这是一个非常基本的例子:

+---------+-------------------------+
| row_id  | columnvalue             |
+---------+-------------------------+
|       1 | 10                      |
|       2 | 20                      |
|       3 | 30                      |
|       4 | 40                      |
+---------+-------------------------+
SELECT columnvalue FROM table_name WHERE row_id = 1 LIMIT 1

返回: 10

但在少数情况下,它可能会返回:20 或 30 等。

我完全困惑为什么它有时会这样做,并希望对似乎是编程现象的一些见解。

更具体的信息:

SELECT
  USERID, CONCAT( LAST, ', ', FIRST ) AS NAME, COMPANYID 
FROM users, companies 
WHERE users.COMPANYCODE = companies.COMPANYCODE 
AND USERID = 9739 LIMIT 1
mysql> DESCRIBE users;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| USERID     | int(10)     | NO   | PRI | NULL    | auto_increment |
| COMPANYCODE| varchar(255)| NO   | MUL |         |                |
| FIRST      | varchar(255)| NO   | MUL |         |                |
| LAST       | varchar(255)| NO   | MUL |         |                |
+------------+-------------+------+-----+---------+----------------+
mysql> DESCRIBE companies;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| COMPANYID  | int(10)     | NO   | PRI | NULL    | auto_increment |
| COMPANYCODE| varchar(255)| NO   | MUL |         |                |
| COMPANYNAME| varchar(255)| NO   |     |         |                |
+------------+-------------+------+-----+---------+----------------+

结果应该是:9739,"L----, E----",2197结果是什么: 9739, "L----, E----", 3288

基本上,它根据与公司代码的连接返回了错误的公司 ID。 鉴于我们公司的性质,我不能分享比这更多的信息。

我已经运行了这个查询 5k 次,并对可以想象的代码进行了非常修改,以生成第二组结果,但我无法复制它。 我不会很快责怪MySQL - 这已经发生了(尽管很少)超过8年,并且已经用尽了所有其他可能的原因。 我怀疑结果是在运行查询后手动更改的,但时间戳另有说明。

我只是挠头,为什么这可以完美运行 499k 次中的 500k。

现在我们有一个更现实的查询,我立即注意到您正在联接表,不是在主键上,而是在公司代码上。我们确定公司代码是作为公司的唯一索引强制执行的吗?如果找到这样的行,限制 1 将隐藏第二行。

从设计的角度来看,我会在主键上进行联接,以避免重复键的可能性,并将公司代码作为唯一的索引字段放入,仅用于显示和查找。

这种行为要么是由于MySQL中一个非常不可能的严重错误,要么是MySQL返回在语句运行时有效的结果,并且有一些其他软件正在清理显示的结果。

可以考虑的一种可能性是,在执行 SQL 语句时,该行已被修改(由其他语句修改),然后该行稍后再次更改。(这是我们对MySQL返回意外结果的最可能的解释。

LIMIT 1 子句的使用很奇怪,因为如果谓词唯一标识一行,则不需要 LIMIT 1 ,因为查询保证返回不超过一行。

这使我怀疑row_id不是唯一的,并且查询实际上返回了多行。使用 LIMIT 子句,无法保证将返回哪些行(没有 ORDER BY 子句)。

否则,最有可能的罪魁祸首是过时的缓存内容或代码中的其他问题。


更新

前面的答案基于给出的示例查询;我故意省略了EMP是一个正在执行 JOIN 的视图的可能性,因为问题最初说它是一个表,而示例查询只显示一个表。

根据问题中的新信息,我建议您从查询中省略LIMIT 1子句。这将标识查询返回多行。

从表定义中,我们看到数据库没有对 COMPANY 表中的 COMPANYCODE 列强制实施 UNIQUE 约束。

我们还知道,由于数据类型之间的不匹配,没有定义外键。

通常,外键将引用目标表的主键进行定义。

我们希望users表有一个company_id列,该列引用companies表中的id(主键)列。

我们注意到companycode列(int)的数据类型与companies表中主键列的数据类型匹配,并且我们注意到companycode列上的连接条件是匹配的,即使数据类型不匹配,这很奇怪。

发生这种情况有几个原因。我建议你看看你所做的假设。例如:

  • 如果您使用的是GROUP BY并且其中一列不是聚合或分组表达式,您将在该列中获得不可预测的值。请确保使用适当的聚合(如 MAXMIN ),以便在每列上获得可预测的结果。
  • 如果您假设行顺序而不显式表示,并且使用 LIMIT 仅获取第一行,则行的实际返回顺序会因结果的执行计划而异,根据优化器可用的统计信息,在大型结果集中会有所不同。确保在这种情况下使用ORDER BY