2014-10-03 104 views
0
table1 

time   userid id1 id2 
9/1/2014 3:30 user1 123 555 
9/1/2014 3:32 user1 123 555 
9/1/2014 3:13 user1 123 555 
9/1/2014 3:15 user1 123 555 
9/1/2014 3:38 user2 321 555 
9/1/2014 3:21 user2 321 555 
9/1/2014 3:38 user2 456 666 
9/1/2014 3:21 user2 456 666 

table2 

id1 orderid 
321 order1 
123 order2 

解释查询:MySQL查询速度很慢,需要帮助优化

select_type table type possible index key   key_len ref  row  Extra 
SIMPLE  table1 ALL             934420 Using where; Using temporary; Using filesort 
SIMPLE  table2 ref lookupindex lookupindex 33  table1.id1 1 

我table1的具有约十亿行,表2是具有20K行查找表,为了555约为100万行。 id2约占整个表格1的10%。 table2基本上是具有所有id1的查找表。 id1-> orderid有多对一的关系。换句话说,一个id1只属于一个orderid。 除userid外,table2和table1不具有空值。

我想计算每个orderid的唯一用户数。

我的查询需要很长时间才能运行(5小时内没有完成,所以我停止了它),我不知道如何优化它以外的索引。我有table2.id1索引。

select table2.orderid, count(distinct userid) 
from table1 left join table2 on table1.id1 = table2.id1 
where table1.id2="555" 
group by table2.orderid 

用MySQL做了左连接第一或where语句第一?应存储顺序555到不同的表,然后运行他们查询?

+1

fields'table1.id2','table2.orderid' have index? – 2014-10-03 19:23:54

+2

将“EXPLAIN”放在您的查询之前并执行它.MySQL将吐出执行路径和优化你的查询,你可以在加入之前看到它是否先过滤“555”(我非常怀疑它的确如此)。http://dev.mysql.com/doc/refman/5.0/en/explain。 html – JNevill 2014-10-03 19:25:10

+0

[阅读关于'EXPLAIN'](http://dev.mysql.com/doc/refman/5.0/en/using-explain.html)。 – Air 2014-10-03 19:25:15

回答

0

问:MySQL的做LEFT JOIN第一或where语句第一?应该将订单555存储到不同的表中,然后运行它们查询?

理论上,优化器可以自由选择任何执行计划来生成指定的结果。优化器应该足够聪明,可以选择它认为最有效的操作顺序。

在实践中,我们编写语句的方式以及我们提供的索引可能会对MySQL可用的选项产生重大影响。


参见MySQL是选择执行计划,我们可以使用EXPLAIN。这向我们展示了MySQL将执行的操作的摘要。

Understanding the Query Execution Plan

有相应的指标可以使可用到MySQL更有效的访问路径。

没有看到EXPLAIN输出或表的定义以及可用的索引,我们只是在猜测。

鉴于该语句非常慢,我们将冒险猜测合适的索引不可用,其次,MySQL将花费大量时间在GROUP BY操作的“使用filesort”操作上)

也可能会重写该语句以返回等效的结果或几乎等效的结果。我们可以抛出一些建议来“尝试这个”或“尝试”。

但让我们来了解MySQL需要执行的操作。

首先,在id2列上有一个相等谓词。如果这种选择性很好(低于table1总行数的10%或20%,则table1id2作为主要列的索引可能会提供有效的访问,这可能会带来一些性能上的好处。 MySQL可以对索引使用范围扫描操作来快速缩小请求的行,而无需查看表中的每一个翻动行。)

其次,在您的查询中有一个“外部连接”操作来查找在table2中匹配行,在id1列中的等于谓词。因此,table2id1作为主要列的索引可能是有益的。

该查询还访问table2匹配行中的orderid列;如果我们还在索引中包含该列,那么这将成为一个“覆盖索引”,这只是一种简短的说法,即MySQL将能够直接从索引中检索所需的所有值,而无需查找到基础表中的页面。

如果这是被检索很多行,我们可以花很多时间对它们进行排序(由GROUP BY所需的排序操作。)

有很多的信息,我们没有,对orderid列的基数,该列列是否可以为null,userid列的基数,是否可以为null,我们期望返回多少行,等等。


之前我们推出进入调整这个特定的语句,我认为我们需要了解什么问题,这个查询试图回答,并确保该查询将返回其实你正在寻找的答案。我们应该开放探索是否可以从不同的查询返回等价的答案。

它看起来像你想从table2(包括可能的NULL值)orderid不同的列表,但不是所有的,但只有一个子集,满足一定的标准。

有了这样的orderid值一起,你想从表1有在id2列的特定值的行计数(不同userid值的数量)。

例如,如果我们不关心的orderid的NULL值...

(也就是说,将原始查询产生的NULL值,由于外时,有来自行加入表1不具有表2中有匹配的行......对每一行table1不具有匹配的行table2,我们知道table2.orderid将是NULL ...)

除了从计数NULL orderid,以下查询将返回相同的orderid列表并计数...

SELECT b.orderid 
    , COUNT(DISTINCT a.userid) 
    FROM table2 b 
    JOIN table1 a 
    ON a.id1 = b.id1 
    AND a.id2 = '555' 
WHERE b.orderid IS NOT NULL 
GROUP BY b.orderid 

对于查询的最佳性能,我建议对表2的覆盖指标:

ON table2 (orderid, id1) 

和table1的覆盖索引,要么/或两者:

ON table1 (id2, id1, userid) 
ON table1 (id1, id2, userid) 

(这是可能我们可能会让MySQL执行紧索引扫描操作来满足GROUP BY,而不是昂贵的临时表(“使用filesort;使用临时“)

我们很想看到的是从EXPLAIN该查询的输出,并为原始查询。

(如果我们确实需要NULL值计数orderid,我们可以编写另一个查询来单独获取它们。)

+0

非常感谢你的解释。我更新了我原来的帖子,它有一些关于说明查询和表格本身的信息 – 2014-10-03 21:43:29

2

问题是你的独特的操作,一个是非常昂贵的。您可以通过在userid上添加一个索引来提高效率,两个键也应该有一个索引。我不确定你在功能上做什么,但也可能有别的选择。

1

这基本上是你的查询:

select t2.orderid, count(distinct t1.userid) 
from table1 t1 left join 
    table2 t2 
    on t1.id1 = t2.id1 
where t1.id2 = 555 
group by t2.orderid; 

首先,你不需要left join,大概是因为你被列在第二分组表中。如果table1相当大,这可能会有很大帮助。所以,写不加入该查询:

select t2.orderid, count(distinct t1.userid) 
from table1 t1 join 
    table2 t2 
    on t1.id1 = t2.id1 
where t1.id2 = 555 
group by t2.orderid; 

其次,你想在table1(id2, id1, userid)table2(id1, orderid)的索引。

根据数据的性质,可能会有一些其他优化。例如,table1是否包含多个userid或是distinct是否为join的人工产物?

0

首先,你指望所有不同值的用户ID和ID1(不参加),然后从表1加入计数值与表2

SELECT orderid, a.sum 
FROM table2 
INNER JOIN  
(SELECT id1, COUNT(DISTINCT userid) as sum FROM table1 WHERE id2 = '555' GROUP BY id1) a 
ON table2.id1 = a.id1