2013-05-09 55 views
1

我有一個表rating略有小於300k行和SQL查詢:慢SQL查詢加入

SELECT rt1.product_id as id1, rt2.product_id as id2, sum(1), sum(rt1.rate-rt2.rate) as sum 
FROM rating as rt1 
JOIN rating as rt2 ON rt1.user_id = rt2.user_id AND rt1.product_id != rt2.product_id 
group by rt1.product_id, rt2.product_id 
LIMIT 1 

的問題是..它真的很慢。用limit 1執行它需要36秒,而我需要無限制地執行它。 正如我想象的那樣,由GROUP BY部分引起的放緩。無論從哪個表中rt1或rt2,按一列進行分組都可以正常工作。 我也嘗試過索引,我已經爲user_id,product_id,rate和(user_id,product_id)創建了索引。

EXPLAIN對我也不太瞭解。

id  select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE rt1  ALL  PRIMARY,user_id,user_product NULL NULL NULL 289700 Using temporary; Using filesort 
1 SIMPLE rt2  ref  PRIMARY,user_id,user_product user_id  4 mgrshop.rt1.user_id  30 Using where 

我需要這個執行一次才能生成一些數據,所以獲得最佳時間並不重要,但合理。

任何想法?

編輯。

全表架構

CREATE TABLE IF NOT EXISTS `rating` (
    `user_id` int(11) NOT NULL, 
    `product_id` int(11) NOT NULL, 
    `rate` int(11) NOT NULL, 
    PRIMARY KEY (`user_id`,`product_id`), 
    KEY `user_id` (`user_id`), 
    KEY `product_id` (`product_id`), 
    KEY `user_product` (`user_id`,`product_id`), 
    KEY `rate` (`rate`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8; 
+0

請寄出兩張表的完整模式。 – mbarlocker 2013-05-09 18:08:02

+0

它只是一張桌子,但自己加入了。 – 2013-05-09 18:11:59

回答

0

首先我通過臨時表完成它。 第一次選擇的行沒有分組,並把它們放在一個專門爲它設計的表格中。我得到了11kk行。然後我將它們從臨時表中分組並放入決賽桌。

然後,我也試圖做到這一點,而不創建任何其他表,它也爲我工作。

SELECT id1, id2, sum(count), sum(sum) 
FROM (SELECT rt1.product_id as id1, rt2.product_id as id2, 1 as count, rt1.rate - rt2.rate as sum 
     FROM rating as rt1 
     JOIN rating as rt2 ON rt1.user_id = rt2.user_id AND rt1.product_id != rt2.product_id) as temptab 
GROUP BY id1, id2 

最後得到大約19k行。

執行時間:35.8669 對我的一次性數據生成情況不錯。

0

你的問題是在加入,特別是AND rt1.product_id != rt2.product_id。 假設用戶已經評定了100個產品,那麼對於該用戶,該查詢將在生成組之前生成99,000行。對於100個評分中的每一個,該表格都被重新連接99次。

你試圖用這個查詢來回答什麼問題?根據這一點,可能會有一些更有效的方法。它很難說出你在這裏想要達到的目標。

+0

我想得到每兩個產品的評價之間的差異(總和和計數)之間的差異。 – 2013-05-09 18:30:29

+0

通過在兩個獨立的子查詢中對數據進行兩次聚合,然後比較結果,您可能會獲得更好的性能。以這個SQL小提琴爲例。 http://sqlfiddle.com/#!8/9ad8c/4 注意:我仍然不清楚你所尋找的答案恰恰是「平均數(和數)差異」並不代表我的任何東西,但希望這能幫助你指出正確的方向。 – 2013-05-09 18:47:22

+0

我不能這樣做,因爲每個用戶都會計算差異,對於他評分的每兩個產品都會計算差異。您錯過了用戶問題。我可以看到我在之前的評論中沒有提到這一點。認爲它是確定的,因爲它沒有任何其他意義。 – 2013-05-09 18:55:06

0

除了什麼Declan_K提及您的交叉聯接的結果集,可能是10萬行,你知道它之前,你可以減少下來顯著改變,只是

rt1.product_id < rt2.product_id

代替

rt1.product_id!= rt2.product_id

原因......因爲它們是同一個表/記錄,你將只能通過他們需要循環一次的RT1.product_ID。因爲它不是最高的,所以你已經有了比較高的一部分。目前的情況是,如果你沒有(單個用戶)有5個產品(1-5),你會得到的

(1,2) (1,3) (1,4) (1,5) 
(2,1) (2,3) (2,4) (2,5) 
(3,1) (3,2) (3,4) (3,5) 
(4,1) (4,2) (4,3) (4,5) 
(5,1) (5,2) (5,3) (5,4) 

結果通過改變到小於,你會消除重複如1 ,2對2,1 1,3對3,1

(1,2) (1,3) (1,4) (1,5) 
     (2,3) (2,4) (2,5) 
       (3,4) (3,5) 
        (4,5) 

只是一個較小的結果集,這是一個人只有5個產品。

+0

它不解決計算複雜度問題,只是將生成的數據除以2.自連接的笛卡爾乘積仍然是O(n^2)複雜度。 – 2013-05-14 09:00:19

0

我的解決方案不是最簡單的,但它應該解釋一點,並加快您的查詢時間。

當您加入MySQL時,會創建一個臨時表。放入該臨時表中的行越多,它就越有可能進入磁盤。磁盤很慢。新的臨時表沒有索引。沒有指數的查詢很慢。

EXPLAIN語句的第一行顯示查詢將首先加入,創建一大堆行,並將其粘貼到臨時表中,然後按產品id進行分組。 key列爲空,表明它不能使用密鑰。

我的解決方案是創建另一個表。這個其他表格由JOIN中的所有相關列組成。您需要批量作業來更新後臺中的表格。這會導致稍微陳舊的數據,但運行速度會更快。

CREATE TABLE `rate_tmp` (
    userid ..., 
    id1 ..., 
    id2 ..., 
    rate1 ..., 
    rate2 ..., 
    PRIMARY KEY (id1, id2, userid) 
) 

主鍵上的順序非常重要。然後,您的查詢看起來是這樣的:

SELECT userid, id1, id2, sum(1), sum(rate1-rate2) as sum 
from rate_tmp 
group by id1, id2; 

應該在這一點上跑得非常快,因爲當表仍保存在磁盤上,MySQL不會有在查詢時將數據寫入到磁盤。它也可以,更重要的是,使用臨時表上的預定義索引。

+0

正如我在我的回答中提到的那樣,我已經嘗試過了,它確實有效。但該答案中提到的查詢也具有類似的性能。 – 2013-05-10 08:39:48

+0

臨時表的模式是什麼?您是否已經進入臨時表或後臺工作? – mbarlocker 2013-05-10 15:48:35

+0

我的臨時表看起來就像目標表。我只是沒有分組,所以它沒有(product1_id,product2_id)主鍵,因爲它不是唯一的。 – 2013-05-11 14:10:57