我有一个问题给我带来了问题,我无法理解为什么MySQL的查询优化器的行为方式如此.这是背景信息:
我有3张桌子.两个相对较小,一个较大.
表1(非常小,727行):
CREATE TABLE
ipa(
ipa_idint(11)NOT NULL AUTO_INCREMENT,
ipa_codeint(11)DEFAULT NULL,
ipa_namevarchar(100)DEFAULT NULL,
payorcodevarchar(2)DEFAULT NULL,
compidint(11)
DEFAULT'2'PRIMARY KEY(ipa_id),
KEYipa_code(ipa_code))ENGINE = MyISAM数据
表2(小,59455行):
CREATE TABLE
assign_ipa(
assignidint(11)NOT NULL AUTO_INCREMENT,
ipa_idint(11)NOT NULL,
useridint(11)NOT NULL,
usernamevarchar(20)DEFAULT NULL,
compidint(11)DEFAULT NULL,
PayorCodechar(10)DEFAULT NULL
PRIMARY KEY(assignid),
UNIQUE KEYassignid(assignid,ipa_id),
KEYipa_id(ipa_id)
)ENGINE = MyISAM
表3(大,24,711,730行):
CREATE TABLE
master_final(
IPAint(11)DEFAULT NULL,
MbrCtsmallint(6)DEFAULT'0',
PayorCodevarchar(4)DEFAULT'WC',
KEYidx_IPA(IPA)
)ENGINE = MyISAM DEFAULT
现在进行查询.我正在使用前两个较小的表进行3向连接,以便在其中一个索引值上对大表进行子集化.基本上,我得到一个用户的ID列表,SJOnes并查询这些ID的大文件.
mysql> explain
SELECT master_final.PayorCode,sum(master_final.Mbrct)AS MbrCt
FROM master_final
INNER JOIN ipa ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
WHERE assign_ipa.username ='SJones'GROUP
BY master_final.PayorCode,master_final.ipa\G;
************* 1.行*************
id:1
select_type:SIMPLE
表:master_final
类型:ALL
possible_keys:idx_IPA
key:NULL
key_len:NULL
ref:NULL
行:24711730
额外:使用临时; 使用filesort
************* 2. row *************
id:1
select_type:SIMPLE
表:ipa
类型:ref
possible_keys:PRIMARY,ipa_code
键: ipa_code
key_len:5
ref:wc_test.master_final.IPA
rows:1
Extra:使用where
************* 3. row *************
id:1
select_type:SIMPLE
表:assign_ipa
类型:ref
possible_keys:ipa_id
key:ipa_id
key_len:4
ref:wc_test.ipa.ipa_id
rows:37
Extra:使用
set 中的3行(0.00秒)
此查询需要永远(如30分钟!).解释声明告诉我为什么,它正在对大表进行全表扫描,即使有一个非常好的索引.它没有使用它.我不明白这一点.我可以查看查询,看看它只需要查询大表中的几个ID.如果我能做到,为什么MySQL的优化器不能这样做呢?
为了说明,这里是与'SJones'相关联的ID:
mysql> select username,ipa_id来自assign_ipa,其中username ='SJones';
+ ---------- + -------- +
| 用户名| ipa_id |
+ ---------- + -------- +
| SJones | 688 |
| SJones | 689 |
+ ---------- + -------- +
2行(0.02秒)
现在,我可以重写查询,将where子句中的用户名替换为ipa_id值.对我来说,这相当于原始查询.MySQL看待它的方式不同.如果我这样做,优化器会使用大表上的索引.
mysql>说明
SELECT master_final.PayorCode,sum(master_final.Mbrct)AS MbrCt
FROM master_final
INNER JOIN ipa ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
*WHERE assign_ipa.ipa_id in('688' ,'689')*
GROUP BY master_final.PayorCode,master_final.ipa\G;
************* 1.行*************
id:1
select_type:SIMPLE
表:ipa
类型:范围
possible_keys:PRIMARY,ipa_code
键:PRIMARY
key_len :4
ref:NULL
行:2
额外:使用where; 使用临时; 使用filesort
************* 2. row *************
id:1
select_type:SIMPLE
表:assign_ipa
类型:ref
possible_keys:ipa_id
key:ipa_id
key_len :4
ref:wc_test.ipa.ipa_id
rows:37
Extra:使用where
************* 3. row *************
id:1
select_type: SIMPLE
表:master_final
类型:ref
possible_keys:idx_IPA
键:idx_IPA
key_len:5
ref:wc_test.ipa.ipa_code
行:34953
额外:使用
设置中的3行(0.00秒)
我唯一改变的是where子句甚至没有直接击中大表.然而,优化器使用大表上的索引'idx_IPA',不再使用全表扫描.像这样重写时的查询非常快.
好的,那是很多背景知识.现在我的问题.为什么where子句对优化器很重要?where子句将从较小的表返回相同的结果集,但是根据我使用的结果,我得到了截然不同的结果.显然,我想使用包含用户名的where子句,而不是尝试将所有关联的ID传递给查询.如上所述,这是不可能的?
谢谢你坚持我.我知道这是一个非常长的问题.
不太确定我是否正确,但我认为这里发生了以下情况。这:
WHERE assign_ipa.username = 'SJones'
Run Code Online (Sandbox Code Playgroud)
可能会创建临时表,因为它需要全表扫描。临时表没有索引,它们往往会减慢速度。
第二种情况
INNER JOIN ipa ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
WHERE assign_ipa.ipa_id in ('688','689')
Run Code Online (Sandbox Code Playgroud)
另一方面允许连接索引,速度很快。此外,还可以将其转化为
SELECT .... FROM master_final WHERE IDA IN (688, 689) ...
Run Code Online (Sandbox Code Playgroud)
我认为 MySQL 也在这样做。
在 allocate_ipa.username 上创建索引可能会有所帮助。
编辑
我重新思考了这个问题,现在有了不同的解释。
原因当然是缺少索引。这意味着MySQL不知道查询assign_ipa的结果有多大(MySQL不存储计数),因此它首先从连接开始,在那里它可以中继键。
这就是解释日志的第 2 行和第 3 行告诉我们的内容。
之后,它尝试通过 allocate_ipa.username 过滤结果,该用户名没有键,如第 1 行所述。
一旦有索引,它就会首先过滤 allocate_ipa,然后使用相应的索引进行连接。
| 归档时间: |
|
| 查看次数: |
349 次 |
| 最近记录: |