2012-10-17 45 views
6

我有一个表foo与(其他20个)列bar,bazquux与索引bazquux。该表有500k行。为什么MAX()比ORDER BY ... LIMIT 1慢100倍?

为什么以下查询在速度上差异如此之大?查询A需要0.3s,而查询B需要28s。

查询

select baz from foo 
    where bar = :bar 
    and quux = (select quux from foo where bar = :bar order by quux desc limit 1) 

解释

id select_type table type possible_keys key  key_len ref  rows Extra 
1 PRIMARY  foo  ref  quuxIdx   quuxIdx 9  const 2  "Using where" 
2 SUBQUERY foo  index NULL   quuxIdx 9  NULL 1  "Using where" 

查询乙

select baz from foo 
    where bar = :bar 
    and quux = (select MAX(quux) from foo where bar = :bar) 

说明

id select_type table type possible_keys key  key_len ref  rows Extra 
1 PRIMARY  foo  ref  quuxIdx   quuxIdx 9  const 2  "Using where" 
2 SUBQUERY foo  ALL  NULL   NULL NULL NULL 448060 "Using where" 

我使用MySQL 5.1.34。

+0

'LiMIT 1'意味着采取1行并停止,不是吗?查询B是O(n * m) – jondinham

+2

@PaulDinh似乎两个查询的结果都是相同的,最有可能与操作顺序有关,第一种情况下它通过quux和搜索栏从结果中快速排序(快速)搜索栏(需要检查整个表)从未分类,然后排序,以找到最大 –

+0

@Viktor,你可以请显示 '解释选择baz从foo 其中bar =:bar 和quux =(选择quux从foo其中quux = MAX(quux)and bar =:bar)' '说明从foo中选择baz 其中bar =:bar 和quux =(从foo选择quux,其中quux = MAX(quux)和bar =:bar limit 1)' –

回答

6

您应该在(bar, quux)上添加索引。

没有这个索引,MySQL无法看到如何有效地执行查询,所以它必须从各种低效的查询计划中进行选择。

在第一个示例中,它扫描quux索引,并找到找到的每一行,在原始表中查找bar的对应值。这需要两倍的时间来检查每一行,但幸运的是,具有正确值bar的行在其扫描开始附近,因此可以停止。这可能是因为您搜索的bar的值经常出现,所以幸运的几率非常高。因此,在查找匹配之前,可能只需检查少量行,即使检查每行需要两倍的时间,但只检查了几行的事实可以大大节省整体资源。由于您没有bar上的索引,因此MySQL并不会提前知道值:bar会频繁发生,因此无法知道此查询会很快。

在第二个示例中,它使用一个不同的计划,它总是扫描整个表。每行都直接从表中读取,而不使用索引。这意味着每行读取速度都很快,但由于行数很多,所以整体速度很慢。如果在:bar上没有匹配的行,这将是更快的查询计划。但是,如果大约1%的行具有期望值bar,则与上述计划相比,使用此查询计划的速度将会(非常)大约慢100倍。由于您在bar上没有索引,因此MySQL并不提前知道这一点。

您也可以只添加缺少的索引,然后两个查询将更快

+1

似乎OP问为什么相同的结果有如此剧烈的差异 –

+0

所以'从foo中选择quux,其中quux = MAX(quux)和bar =:bar'快于'从foo选择MAX(quux)where bar =:bar'如果quux被索引int并且bar是文本? –

+0

nvm它给出了不同的结果:) –

相关问题