为什么 MySQL 不在大表上使用我的 PRIMARY 索引?

问题描述 投票:0回答:2

我有这样的疑问:

EXPLAIN SELECT b.* FROM bills b WHERE
(b.id IN (SELECT u.id FROM bills u WHERE u.updated BETWEEN '2023-12-19 14:17:48' AND '2023-12-19 14:27:40' AND u.organisation_id = 'ABC123')
OR b.id IN (SELECT c.id FROM bills c WHERE c.created BETWEEN '2023-12-19 14:17:48' AND '2023-12-19 14:27:40' AND c.organisation_id = 'ABC123'))

但结果是

id 选择类型 桌子 分区 类型 可能的键 key_len 参考 过滤 额外
1 小学 t0 全部 10402901 100 使用地点
3 子查询 c 范围 主要,已创建组织 组织_创建 41 1 100 使用地点;使用索引
2 子查询 范围 主要,已更新,组织已创建 更新了 5 4 1.87 使用索引条件;使用地点

任何人都可以帮助我理解为什么第一行没有使用

PRIMARY
索引(仅在
id
上)?我尝试强制该索引与
FORCE INDEX (PRIMARY)
一起使用,但它仍然没有被使用。另外,是否可以将其重写为单个查询,以便使用 JPA 运行得更快(因此
UNION
不幸的是不可用)?

sql mysql query-optimization
2个回答
0
投票

不幸的是,带有

OR
的查询表达式很难优化。

想想电话簿。它就像按顺序在

(last_name, first_name)
上定义的索引。

如果我搜索

last_name='Karwin'
,索引有利于搜索。

如果我只搜索

first_name='Bill'
,索引没有帮助,因为索引不是按名字排序的。许多条目的名字都为“Bill”,并且它们可以位于任何地方。即使我使用 FORCE INDEX,索引也永远无法使用。

如果我有第二本由

(first_name, last_name)
组织的电话簿,那么我对
first_name='Bill'
的搜索可以有效地对该索引的第一列执行搜索。但仅搜索姓氏无法使用该索引。

如果我想搜索

first_name='Bill' AND last_name='Karwin'
那么任一索引都会有帮助。它将有效地搜索第一列,然后在匹配项中,它将同样有效地搜索第二列。

但是如果我想搜索

first_name='Bill' OR last_name='Karwin'
那么两个索引都不能同时执行这两项操作。如果我们假设一次只能使用一个索引,那么我们就会陷入困境。不管怎样,都需要从头到尾地阅读电话簿。

这就是为什么 FORCE INDEX 不能解决这个问题。当指数根本无法完成这项工作时,坚持是没有好处的。无论如何,这不是 FORCE INDEX 的意思。这仅意味着优化器应将表扫描视为比命名索引更昂贵的选择,因此它更愿意使用索引 如果可能的话。

标准解决方案是将使用

OR
表达式的查询分解为两个查询,然后每个查询可以使用不同的索引,并通过
UNION
重新组合每个查询的结果。

但是标准JPA不支持

UNION
* 因此,如果您需要
@Query( ..., nativeQuery = true )
,则必须使用
UNION


* 我已了解到 EclipseLink JPA 确实支持

UNION
,参见。 https://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#UNION但是文档非常薄弱,显然他们的wiki将被关闭。我不太愿意使用 EclipseLink,它似乎是半生不熟的。


0
投票

查询可以简化,因为id是一个PK,并且你从同一个表中选择id,你不需要这里子查询。

SELECT b.* FROM bills b 
 WHERE

 (b.updated BETWEEN '2023-12-19 14:17:48' AND '2023-12-19 14:27:40'
 OR 
 b.created BETWEEN '2023-12-19 14:17:48' AND '2023-12-19 14:27:40'
 ) AND b.organisation_id = 'ABC123'

尝试按创建的、更新的和organization_id列建立索引。

按organization_id建立索引绝对是最高效的

© www.soinside.com 2019 - 2024. All rights reserved.