选择查询和'order by'子句的MySQL问题

Mar*_*jaR 5 mysql mariadb

我们遇到了一个SELECT针对MySQL数据库(InnoDB)的查询的奇怪问题.

以下查询错误地返回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
Run Code Online (Sandbox Code Playgroud)

而以下查询正确返回没有匹配的记录:

select `ID` 
from `AccessTables` 
where `numTableID` = 14 
    AND `numUserCatID` IN (7,253) 
    AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2) 
limit 1
Run Code Online (Sandbox Code Playgroud)

如您所见,这些查询之间的唯一区别是"order by"子句.

ID查询中请求的列是表的自动生成的主键列.

第一个查询中返回的记录是一个记录,如果'或'子句周围没有括号,则会找到该记录.但是在查询的那一部分周围有括号,所以我不明白为什么在这里返回此记录.然后,只有在查询中存在"order by"子句时.

正在使用的MySQL版本是:MySQL服务器: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);
Run Code Online (Sandbox Code Playgroud)

(编辑nr.2:没有MariaDB,但.... http://sqlfiddle.com/#!2/2a922/8)

编辑nr.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;
Run Code Online (Sandbox Code Playgroud)

输出:

"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"
Run Code Online (Sandbox Code Playgroud)

QUERY2:

EXPLAIN EXTENDED SELECT `ID` 
FROM `AccessTables` 
WHERE `numTableID` = 14 
    AND `numUserCatID` IN (7,253) 
    AND (`numUpdateCat` = 2 OR `numUpdateItems` = 2) 
LIMIT 1;
Run Code Online (Sandbox Code Playgroud)

输出:

"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"
Run Code Online (Sandbox Code Playgroud)

QUERY3:

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;
Run Code Online (Sandbox Code Playgroud)

输出:

"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"
Run Code Online (Sandbox Code Playgroud)

编辑nr.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;
Run Code Online (Sandbox Code Playgroud)

输出:

"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"
Run Code Online (Sandbox Code Playgroud)

因此,返回正确结果的查询(找到0条记录)似乎与numUserCatID上的索引一起使用,而返回错误结果的查询(找到1条记录)似乎与numTableID上的索引一起使用.

奇怪...!

编辑nr.5:
按另一列排序,例如.dateInsert(表示记录插入表中的时刻的日期/时间戳)也会更改查询结果.
然后再次没有返回记录,并且使用的索引再次是numUserCatID上的索引.

我们使用' IDasc '命令,因为我们假设ID总是代表记录插入数据库的顺序.
但是dateInsert在我们的例子中基本相同.

使用常规键列进行排序而不是主键时,是否会在大型数据库中出现性能损失?

ype*_*eᵀᴹ 2

如果真的发生这种情况,那就是一个错误。对于“真的”,我的意思是这些是您发送到数据库服务器的确切查询,并且基础表尚未同时更新。

类似(但不相同)的问题在这里:MDEV-2662

请将问题报告给 MariaDB 团队。


要解决眼前的问题,请尝试重写查询,例如不使用IN, 替换;

AND numUserCatID IN (7,253) 
Run Code Online (Sandbox Code Playgroud)

和:

AND (numUserCatID = 7 OR numUserCatID = 253)
Run Code Online (Sandbox Code Playgroud)

并检查是否得到相同的错误结果。