【发布时间】:2014-07-17 20:11:54
【问题描述】:
我们遇到了一个针对 MySQL 数据库 (InnoDB) 的 SELECT 查询的奇怪问题。
以下查询错误地返回 1 条匹配记录:
select `ID`
from `AccessTables`
where `numTableID` = 14
AND `numUserCatID` IN (7,253)
AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2)
order by `ID` asc
limit 1
而以下查询正确返回没有匹配的记录:
select `ID`
from `AccessTables`
where `numTableID` = 14
AND `numUserCatID` IN (7,253)
AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2)
limit 1
如您所见,这些查询之间的唯一区别是“order by”子句。
查询中请求的ID 列是表的自动生成的主键列。
在第一个查询中返回的记录是在“or”子句周围没有括号时可以找到的记录。 但是查询的那部分周围有括号,所以我不明白为什么这里会返回这条记录。然后仅当查询中有“order by”子句时。
使用的 MySQL 版本为:MySQL Server: 5.5.32-MariaDB-log
这里有人能解释一下这个问题吗?提前致谢。
(编辑:省略括号确实会返回一行,但这与第一个查询返回的行不同)
insert into `AccessTables`(`ID`,`numUserCatID`,`numTableID`,`numUpdateCat`,`numPublishCat`,`numUpdateItems`,`dateInsert`,`dateUpdate`,`numInsertAuthorID`,`numUpdateAuthorID`,`numViewItems`) values (71,15,14,0,0,2,'2008-03-13 23:38:47','2013-04-04 09:34:36',0,513,2);
(编辑编号 2:没有 MariaDB,但是 ....http://sqlfiddle.com/#!2/2a922/8)
编辑编号。 3、针对真正的 MariaDB 运行这些查询:
查询1:
EXPLAIN EXTENDED SELECT `ID`
FROM `AccessTables`
WHERE `numTableID` = 14
AND `numUserCatID` IN (7,253)
AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2)
ORDER BY `ID` ASC
LIMIT 1;
输出:
"id" "select_type" "table" "type" "possible_keys" "key" "key_len" "ref" "rows" "filtered" "Extra"<br />
"1" "SIMPLE" "AccessTables" "range" "numUserCatID,numTableID,numUpdateCat,numUpdateItems" "numTableID" "8" \N "136" "11.03" "Using where"
查询2:
EXPLAIN EXTENDED SELECT `ID`
FROM `AccessTables`
WHERE `numTableID` = 14
AND `numUserCatID` IN (7,253)
AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2)
LIMIT 1;
输出:
"id" "select_type" "table" "type" "possible_keys" "key" "key_len" "ref" "rows" "filtered" "Extra"<br />
"1" "SIMPLE" "AccessTables" "range" "numUserCatID,numTableID,numUpdateCat,numUpdateItems" "numUserCatID" "8" \N "20" "75.00" "Using index condition; Using where"
查询3:
EXPLAIN EXTENDED SELECT `ID`
FROM `AccessTables`
WHERE `numTableID` = 14
AND (numUserCatID = 7 OR numUserCatID = 253)
AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2)
ORDER BY `ID` ASC
LIMIT 1;
输出:
"id" "select_type" "table" "type" "possible_keys" "key" "key_len" "ref" "rows" "filtered" "Extra"<br />
"1" "SIMPLE" "AccessTables" "range" "numUserCatID,numTableID,numUpdateCat,numUpdateItems" "numTableID" "8" \N "136" "11.03" "Using where"
编辑编号。 4: 删除“limit 1”与删除“order by”的结果相同:找不到行。
查询 4:
EXPLAIN EXTENDED SELECT `ID`
FROM `AccessTables`
WHERE `numTableID` = 14
AND `numUserCatID` IN (7,253)
AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2)
ORDER BY `ID` ASC;
输出:
"id" "select_type" "table" "type" "possible_keys" "key" "key_len" "ref" "rows" "filtered" "Extra"<br />
"1" "SIMPLE" "AccessTables" "range" "numUserCatID,numTableID,numUpdateCat,numUpdateItems" "numUserCatID" "8" \N "20" "75.00" "Using index condition; Using where; Using filesort"
因此,返回正确结果(找到 0 条记录)的查询似乎适用于 numUserCatID 上的索引,而返回错误结果(找到 1 条记录)的查询似乎适用于 numTableID 上的索引。
奇怪...!
编辑编号。 5:
按另一列排序,例如。 dateInsert(这是一个日期/时间戳,表示将记录插入表中的时刻),也会更改查询结果。
然后再次没有返回任何记录,并且使用的索引再次是 numUserCatID 上的索引。
我们使用“order by ID asc”是因为我们假设 ID 始终代表记录插入数据库的顺序。
但是 dateInsert 在我们的例子中基本上是一样的。
在大型数据库中使用常规键列而不是主键进行排序时会不会有性能损失?
【问题讨论】:
-
我们是否有机会获得该行的数据副本,或者至少获得与问题相关的列?
-
这两个查询是自动生成的(在某些应用程序中)还是您在受信任的环境中运行它们(例如,使用 mysql-cli)并看到奇怪的结果?
-
这似乎是一个错误。向 MariaDB 发送错误报告。
-
你能告诉我们返回的行吗? (我的意思是所有列,而不仅仅是
ID。 -
表使用的是哪个引擎?
AccessTables是基表还是视图?