我在使用 MySQL 执行以下任务时遇到问题。我有一个表记录(id,企业,部门,状态)。其中 id 是主键,企业和部门是外键,status 是一个整数值(0-CREATED、1-APPROVED、2-REJECTED)。
现在,通常应用程序需要针对具体的企业、部门和状态进行过滤:
SELECT * FROM Records WHERE status = 0 AND enterprise = 11 AND department = 21
ORDER BY id desc LIMIT 0,10;
order by 是必需的,因为我必须向用户提供最新的记录。对于这个查询,我创建了一个索引(企业、部门、状态),一切正常。但是,对于某些特权用户,应省略状态:
SELECT * FROM Records WHERE enterprise = 11 AND department = 21
ORDER BY id desc LIMIT 0,10;
这显然破坏了索引 - 它仍然适合过滤,但不适合排序。所以我该怎么做?我不想创建单独的索引(企业、部门),那么如果我像这样修改查询呢:
SELECT * FROM Records WHERE enterprise = 11 AND department = 21
AND status IN (0,1,2)
ORDER BY id desc LIMIT 0,10;
MySQL现在确实使用了索引,因为它提供了状态值,但是按主键排序的速度有多快?它会获取每个可用状态的最近 10 个值,然后合并它们,还是首先将每个状态的 id 合并在一起,然后才获取前 10 个值(我猜这样会慢得多)。
最佳答案
所有查询都将从一个复合查询中受益:
INDEX(enterprise, department, status, id)
企业
和部门
可以交换,但保持其余列的顺序。
第一个查询将在 WHERE
和 ORDER BY
中使用该索引,从而无需扫描表或进行排序即可找到 10 行。
第二个查询缺少status
,因此我的索引不太完美。这样会更好:
INDEX(enterprise, department, id)
此时,它的工作原理就像上面一样。 (注意:如果表是 InnoDB,那么这个 3 列索引与您的 2 列 INDEX(enterprise, Department)
相同 - PK 是默默包含在内的。)
由于 IN
,第三个查询变得更加危险。尽管如此,我的 4 列索引几乎是最好的。它将使用前 3 列,但无法执行 ORDER BY id
,因此它不会使用 id
。并且它无法达到LIMIT
。因此,EXPLAIN
将显示使用临时和/或使用文件排序
。别担心,性能应该还是不错的。
我的第二个索引对于第三个查询来说不太好。
查看我的Index Cookbook .
“按 id 排序的速度有多快”?这取决于两件事。
- 是否可以避免排序(见上文);
- 查询中有多少行没有
LIMIT
; - 您是否选择
TEXT
列。
我很小心地说了INDEX
是否一直使用到ORDER BY
,在这种情况下没有排序,以及LIMIT
被折叠起来。否则,所有 行(过滤后)都将写入临时表并排序,然后 10 行被剥离。
我刚才提到的“临时表”对于各种复杂的查询是必需的,例如带有子查询、GROUP BY
、ORDER BY
的查询。 (正如我已经暗示的,有时可以避免使用临时表。)无论如何,临时表有两种类型:MEMORY
和 MyISAM
。 MEMORY
是有利的,因为它更快。然而,TEXT
(以及其他一些东西)阻止了它的使用。
如果使用MEMORY
,那么使用文件排序
是一个用词不当——排序实际上是内存中排序,因此速度相当快。对于 10 行(甚至 100 行),所花费的时间可以忽略不计。
关于mysql - 使用带 IN 子句的索引并按主键排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39367529/