2014-10-03 89 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