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
解释查询:
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有大约10亿行,table2是有20k行的查找表,而555行是大约1亿行。 id2约占table1总数的10%。 table2基本上是具有所有id1的查找表。 ID1-> orderid有多对一的关系。换句话说,一个id1只属于一个orderid。 除了userid之外,table2和table1没有null值。
我想为每个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存储到不同的表中然后运行它们查询吗?
答案 0 :(得分:2)
问题是你的独特操作,一个是非常昂贵的。您可以通过在userid上放置索引来提高效率,这两个键也应该有一个索引。我不确定你在功能上做了什么,但也许其他东西也可以替代。
答案 1 :(得分: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
的工件吗?
答案 2 :(得分:0)
首先,你计算所有不同的值userid和id1(没有连接),然后将table1中的计数值与table2连接
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
答案 3 :(得分:0)
问:mysql首先执行左连接还是先执行where语句?应该将订单555存储到不同的表中然后运行它们查询吗?
理论上,优化器可以自由选择产生指定结果的任何执行计划。优化器应该足够聪明,可以选择它认为最有效的操作顺序。
实际上,我们编写语句的方式以及我们提供的索引可能会对MySQL可用的选项产生重大影响。
要查看MySQL正在选择的执行计划,我们可以使用EXPLAIN
。这向我们展示了MySQL将要执行的操作的总结。
Understanding the Query Execution Plan
拥有适当的索引可以为MySQL提供更高效的访问路径。
没有看到EXPLAIN输出,或者表的定义,以及可用的索引,我们只是在猜测。
鉴于该声明非常缓慢,我们将冒险猜测合适的索引不可用,其次,MySQL正在花费大量时间在“{filesort”操作上{{1操作。)
也可能重写该语句以返回等效结果,或者几乎等效的结果。我们可以抛出一些建议“试试这个”或“试试”。
但是让我们了解MySQL需要执行的操作。
首先,GROUP BY
列上有一个等式谓词。如果这是相当有选择性的(少于id2
中总行数的10%或20%,table1
上table1
作为前导列的索引可能会提供有效的访问权限,这可能会带来一些性能上的好处。(这很有效,因为MySQL可以对索引使用范围扫描操作来快速缩小请求的行,而不必查看表中的每个翻转行。)
其次,在您的查询中,有一个“外部联接”操作,用于查找id2
中的匹配行,并在table2
列上使用等式谓词。因此,id1
上的table2
作为主要列的索引可能会有所帮助。
该查询还从id1
的匹配行访问orderid
列;如果我们还在索引中包含该列,那将使它成为“覆盖索引”,这只是一种简短的说法,即MySQL将能够直接从索引中检索所需的所有值,而无需查找到基础表格中的页面。
如果检索到很多行,我们可能会花很多时间对它们进行排序(GROUP BY所需的排序操作。)
我们没有很多信息,关于orderid列的基数,列列是否为null,userid列的基数,是否为null,我们期待的行数要退还,等等。
在我们开始调整此特定语句之前,我认为我们需要了解此查询试图回答的问题,并确保此查询实际上会返回您正在寻找的答案。我们应该开放探索是否可以从不同的查询中返回等效答案。
看起来你想要一个来自table2
的{{1}}的明确列表(包括可能的NULL值),而不是所有这些,只有一个子集,符合某些标准。
除了orderid
值之外,您还需要来自table1中table2
列中具有特定值的行的计数(不同orderid
值的数量)。< / p>
例如,如果我们不关心userid
...
(即,当table1中的行没有table2中的匹配行时,由于外连接而由原始查询产生的NULL值... id2
中的每一行在orderid
中没有匹配的行,我们知道table1
将为NULL ...)
除了NULL orderid的计数之外,以下查询将返回相同的orderid列表并计数......
table2
为了获得该查询的最佳性能,我建议在table2上覆盖索引:
table2.orderid
和table1上的覆盖索引,或者两者之一:
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
(我们可能会让MySQL执行紧密索引扫描操作以满足GROUP BY,而不是昂贵的临时表(“使用filesort;使用临时”)
我们真正希望看到的是ON table2 (orderid, id1)
对该查询和原始查询的输出。
(如果我们确实需要ON table1 (id2, id1, userid)
ON table1 (id1, id2, userid)
的{{1}}值的计数,我们可以编写另一个查询来单独获取它。)