我的查询给了我一些问题,我无法理解MySQL查询优化器的行为方式。这是背景信息:
我有3张桌子。两个相对较小,一个较大。
表1(非常小,727行):
创建表
ipa
(
ipa_id
int(11)NOT NULL AUTO_INCREMENT,
ipa_code
int(11)DEFAULT NULL,
ipa_name
varchar(100)DEFAULT NULL,
payorcode
varchar(2)DEFAULT NULL,
compid
int(11)DEFAULT' 2'
PRIMARY KEY(ipa_id
),
KEYipa_code
(ipa_code
)) ENGINE = MyISAM
表2(小,59455行):
创建表
assign_ipa
(
assignid
int(11)NOT NULL AUTO_INCREMENT,
ipa_id
int(11)NOT NULL,
userid
int(11)NOT NULL,
username
varchar(20)DEFAULT NULL,
compid
int(11)DEFAULT NULL,
PayorCode
char(10)DEFAULT NULL
PRIMARY KEY(assignid
),
独特的钥匙assignid
(assignid
,ipa_id
),
KEYipa_id
(ipa_id
)
)ENGINE = MyISAM
表3(大,24,711,730行):
创建表
master_final
(
IPA
int(11)DEFAULT NULL,
MbrCt
smallint(6)DEFAULT' 0',
PayorCode
varchar(4)DEFAULT' WC',
KEYidx_IPA
(IPA
)
)ENGINE = MyISAM DEFAULT
现在进行查询。我正在使用前两个较小的表进行三向连接,以便在其中一个索引值上对大表进行子集化。基本上,我得到一个用户的ID列表,SJOnes并查询大文件中的那些ID。
的MySQL>解释
SELECT master_final.PayorCode,sum(master_final.Mbrct)AS MbrCt
来自master_final
INNER JOIN ipa ON ipa.ipa_code = master_final.IPA
INNER JOIN assign_ipa ON ipa.ipa_id = assign_ipa.ipa_id
在哪里assign_ipa.username =' SJones'
GROUP BY master_final.PayorCode,master_final.ipa \ G;
的 * ** * ** * ** * ** * 1.行 * ** * ** * ** * * * *
id:1
select_type:SIMPLE
table:master_final
类型:所有
possible_keys:idx_IPA
key:NULL
key_len:NULL
ref:NULL
行: 24711730
额外:使用临时;使用filesort
的 * ** * ** * ** * ** * 2.行 * ** * ** * ** * * * *
id:1
select_type:SIMPLE
表:ipa
类型:参考
possible_keys:PRIMARY,ipa_code
key:ipa_code
key_len:5
ref:wc_test.master_final.IPA
行:1
额外:使用在哪里 的 * ** * ** * ** * ** * 3.行 * ** * ** * ** * * * *
id:1
select_type:SIMPLE
表:assign_ipa
类型:参考
possible_keys:ipa_id
key:ipa_id
key_len:4
ref:wc_test.ipa.ipa_id
行:37
额外:使用在哪里 3行(0.00秒)
此查询需要永远(如30分钟!)。解释说明告诉我为什么,即使有一个非常好的索引,它也会在大表上进行全表扫描。它没有使用它。我不明白这一点。我可以查看查询,看到它只需要查询大表中的几个ID。如果我能做到,为什么MySQL的优化器能做到呢?
为了说明,以下是与' SJones':
相关联的ID的MySQL>从assign_ipa中选择用户名,ipa_id,其中username =' SJones';
+ ---------- + -------- +
|用户名| ipa_id |
+ ---------- + -------- +
| SJones | 688 |
| SJones | 689 |
+ ---------- + -------- +
2行(0.02秒)
现在,我可以重写查询,用if子句中的用户名替换ipa_id值。对我来说,这相当于原始查询。 MySQL看待它的方式不同。如果我这样做,优化器会使用大表上的索引。
的MySQL>解释
SELECT master_final.PayorCode,sum(master_final.Mbrct)AS MbrCt
来自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
关键:主要
key_len:4
ref:NULL
行:2
额外:使用在哪里;使用临时;使用filesort
的 * ** * ** * ** * ** * 2.行 * ** * ** * ** * * * *
id:1
select_type:SIMPLE
表:assign_ipa
类型:参考
possible_keys:ipa_id
key:ipa_id
key_len:4
ref:wc_test.ipa.ipa_id
行:37
额外:使用在哪里 的 * ** * ** * ** * ** * 3.行 * ** * ** * ** * * * *
id:1
select_type:SIMPLE
table:master_final
类型:参考
possible_keys:idx_IPA
key:idx_IPA
key_len:5
ref:wc_test.ipa.ipa_code
行: 34953
额外:使用在哪里 3行(0.00秒)
我唯一改变的是where子句甚至没有直接击中大桌子。然而,优化器使用索引' idx_IPA'在大桌子上,不再使用全表扫描。像这样重写的查询非常快。
好的,那是很多背景知识。现在我的问题。为什么where子句对优化器很重要? where子句将从较小的表返回相同的结果集,但是我得到的结果却大不相同,这取决于我使用的是哪一个。显然,我想使用包含用户名的where子句,而不是尝试将所有关联的ID传递给查询。如上所述,这是不可能的?
感谢您坚持下去。我知道这是一个非常长的问题。
答案 0 :(得分:4)
不太确定我是否正确,但我认为以下情况正在发生。这样:
WHERE assign_ipa.username = 'SJones'
可能会创建一个临时表,因为它需要全表扫描。临时表没有索引,它们往往会减慢很多东西。
第二种情况
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')
另一方面,允许加入索引,这很快。此外,它可以转换为
SELECT .... FROM master_final WHERE IDA IN (688, 689) ...
我认为MySQL也是这样做的。
在assign_ipa.username上创建索引可能会有所帮助。
修改强>
我重新考虑了这个问题,现在有了不同的解释。
当然,原因是缺少索引。这意味着MySQL不知道查询assign_ipa的结果有多大(MySQL不存储计数),所以它首先从连接开始,它可以在密钥上进行中继。
这就是解释日志的第2行和第3行告诉我们的。
之后,它尝试通过assign_ipa.username过滤结果,该行没有密钥,如第1行所述。
只要有索引,它就会首先过滤assign_ipa,然后使用相应的索引加入。
答案 1 :(得分:2)
这可能不是您问题的直接答案,但您可以做的事情很少:
运行ANALYZE_TABLE ...它将更新表统计信息,这对优化程序决定要做的事情有很大影响。
如果您仍然认为联接不符合您的希望(在您的情况下会发生这种情况,因此优化器没有像您期望的那样使用索引),您可以使用STRAIGHT_JOIN ...来自here:“STRAIGHT_JOIN强制优化器按照FROM子句中列出的顺序连接表。如果优化器以非最佳顺序连接表,则可以使用它来加速查询”
对我而言,将“where part”加入连接有时会产生影响并加快速度。例如,您可以写:
...t1 INNER JOIN t2 ON t1.k1 = t2.k2 AND t2.k2=something...
而不是
...t1 INNER JOIN t2 ON t1.k1 = t2.k2 .... WHERE t2.k2=something...
所以这绝对不是解释为什么你有这种行为而只是一些提示。查询优化器是一个奇怪的野兽,但幸运的是有EXPLAIN命令可以帮助你欺骗它以你想要的方式运行。