2011-12-22 76 views
1

編輯:這裏是原始查詢的簡化版本如何使此查詢更有效?

SELECT p.*, shop FROM products p JOIN 
users u ON p.date >= u.prior_login and u.user_id = 22 JOIN 
shops s ON p.shop_id = s.shop_id 
ORDER BY shop, date, product_id; 

(在3.6秒開的475K行的產品表中運行),這是解釋計劃

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE u const PRIMARY,prior_login,user_id PRIMARY 4 const 1 Using temporary; Using filesort 
1 SIMPLE s ALL PRIMARY NULL NULL NULL 90 
1 SIMPLE p ref shop_id,date,shop_id_2,shop_id_3 shop_id 4 bitt3n_minxa.s.shop_id 5338 Using where 

的瓶頸似乎是ORDER BY date,product_id。刪除這兩個排序,查詢運行0.06秒。 (刪除兩者中的任何一個(但不是兩者)幾乎沒有影響,查詢仍然需要3秒以上)。我在產品表中的product_id和date上都有索引。我還在(產品,日期)上添加了沒有任何改進的索引。

newtover暗示問題是INNER JOIN users u1 ON products.date >= u1.prior_login要求防止使用索引上products.date,其執行在〜0.006秒(而不是3.6秒爲原件)的查詢

兩種變化的事實已經被建議給我(而不是從這個線程)。

這一個使用子查詢,這似乎強制秩序的加入

SELECT p.*, shop 
    FROM 
    (
    SELECT p.* 
    FROM products p 
    WHERE p.date >= (select prior_login FROM users where user_id = 22) 
) as p 
    JOIN shops s 
    ON p.shop_id = s.shop_id 
    ORDER BY shop, date, product_id; 

這一個使用WHERE子句做同樣的事情(儘管SQL_SMALL_RESULT的存在不會改變的執行時間,0.006秒沒有它)

SELECT SQL_SMALL_RESULT p . * , shop 
FROM products p 
INNER JOIN shops s ON p.shop_id = s.shop_id 
WHERE p.date >= ( 
SELECT prior_login 
FROM users 
WHERE user_id =22) 
ORDER BY shop, DATE, product_id; 

我的理解是,這些查詢更快的工作是考慮減少其連接到商店表之前的產品表的行相關數量。我想知道這是否正確。

+0

順便說一下,你在哪裏多的product_id行來自您需要排序的'circle_favorited'? – newtover 2011-12-22 16:53:08

+0

@newtover用戶的多個朋友可以喜歡同樣的產品,所以每個不同的朋友,誰收藏與給定的product_id產品生成包含PRODUCT_ID(和朋友的用戶名)新行。收藏夾表中的每一行都包含一個user_id和一個二進制收藏夾值。如果此值爲0,這意味着用戶投下了產品。如果此值爲1,則用戶投票選擇產品。我根據circle_favorited進行排序,以便將投票產品的用戶與投票的用戶分開。 – jela 2011-12-22 18:28:25

回答

1

使用EXPLAIN語句查看執行計劃。您也可以嘗試添加一個索引到products.dateu1.prior_login

也請確保你已經定義了你的外鍵,並且它們被編入索引。

祝你好運。

+0

我在上面添加了解釋計劃。在date和prior_login上添加索引不會改變執行時間。 – jela 2011-12-22 04:18:05

0

我們確實需要一個解釋計劃......但

要非常小心的SELECT * FROM表其中id中(選擇another_table ID)這是一個臭名昭著。一般來說,這些可以由聯接來代替。以下查詢可能會運行,雖然我沒有測試過它。

SELECT shop, 
     shops.shop_id AS shop_id, 
     products.product_id AS product_id, 
     brand, 
     title, 
     price, 
     image AS image, 
     image_width, 
     image_height, 
     0 AS sex, 
     products.date AS date, 
     fav1.favorited AS circle_favorited, 
     fav2.favorited AS session_user_favorited, 
     u2.username AS circle_username 
    FROM products 
     LEFT JOIN favorites fav2 
      ON  fav2.product_id = products.product_id 
      AND fav2.user_id = 22 
      AND fav2.current = 1 
     INNER JOIN shops 
      ON shops.shop_id = products.shop_id 
     INNER JOIN users u1 
      ON products.date >= u1.prior_login AND u1.user_id = 22 
     LEFT JOIN favorites fav1 
      ON products.product_id = fav1.product_id 
     LEFT JOIN friends f1 
      ON f1.star_id = fav1.user_id 
     LEFT JOIN users u2 
      ON fav1.user_id = u2.user_id 
WHERE f1.fan_id = 22 OR fav1.user_id = 22 
ORDER BY shop, 
     DATE, 
     product_id, 
     circle_favorited 
+0

謝謝,我已經添加了一個解釋上述計劃,將考驗你的查詢目前 – jela 2011-12-22 04:16:59

+0

查詢只生產,而不是原來的497條結果。我相信這是考慮WHERE子句,這是限制的結果,只有那些案件中(f1.fan_id = 22或fav1.user_id = 22),當這不會是大多數產品的情況。如果我移動這些標準以使它們成爲'LEFT JOIN friends f1'聲明的一部分,那麼看起來好像我在用戶收到該產品時得到了產品的重複行(即兩行包含圓圈用戶名作爲會話用戶對於該產品),這在原始查詢中沒有發生。查詢耗時3.7秒。 – jela 2011-12-22 04:34:12

+0

我在解釋計劃中沒有看到任何讓我感到無法理解的東西。我所建議的是通過表格構建查詢表,並找出造成最大性能影響的因素。它不應該花太長的時間,因爲你已經有了一個功能性的查詢。 – 2011-12-22 14:33:56

0

的事實查詢,因爲訂貨的緩慢是相當明顯的,因爲它是很難找到,可以更好地適用於這種情況ORDER BY的索引。主要問題是products.date >=比較使用ORDER BY的任何索引中斷。而且由於你有很多數據要輸出,所以MySQL開始使用臨時表進行排序。

我想試圖強制MySQL輸出數據的索引順序已經有所需的順序,並刪除ORDER BY子句。

我不是在電腦前進行測試,但我會怎麼做:

  • 我會做所有的內部連接
  • 那麼我會LEFT JOIN到一個子查詢,這使得下令收藏夾的所有計算通過product_id,circle_favourited(這將提供最後的排序標準)。

所以,問題是如何使數據可以在商店,日期排序,PRODUCT_ID

我去晚了一點寫下來=)

UPD1:

你應該閱讀關於btree索引如何在MySQL中工作的內容。有關於它的mysqlperformanceblog.com一個很好的文章(我目前從移動寫,不必在手的鏈接)。簡而言之,你似乎談論的是根據排序在單個列中的值排列指向行的單列索引。複合索引根據幾列存儲訂單。索引主要用於在明確定義的範圍內進行操作,以便在從指向的行中檢索數據之前獲取大部分信息。索引通常不知道同一張表上的其他索引,因爲它們很少被合併。當沒有更多的信息需要從索引中獲取時,MySQL就開始直接對數據進行操作。

這是日期的索引不能使用product_id上的索引,但(日期,product_id)上的索引可以在日期條件之後的product_id上獲得更多信息(對特定日期的產品id進行排序比賽)。

然而,在日期的範圍的條件(> =)打破此。這就是我所說的。

UPD2:

正如我uderstand問題可以降低到(大部分花在了時間):用戶和

SELECT p.*, shop 
FROM products p 
JOIN users u ON p.`date` >= u.prior_login and u.user_id = 22 
JOIN shops s ON p.shop_id = s.shop_id 
ORDER BY shop, `date`, product_id; 

現在添加一個索引(USER_ID,prior_login)(日期)產品,並嘗試以下查詢:

SELECT STRAIGHT_JOIN p.*, shop 
FROM (
    SELECT product_id, shop 
    FROM users u 
    JOIN products p 
    user_id = 22 AND p.`date` >= prior_login 
    JOIN shops s 
    ON p.shop_id = s.shop_id 
    ORDER BY shop, p.`date`, product_id 
) as s 
JOIN products p USING (product_id); 

如果我是正確的查詢應返回相同的結果,但速度更快。如果您發佈查詢的結果EXPLAIN,那麼這樣做會很好。

+0

如果我理解正確的話,問題是我不能同時使用products.date來限制結果,如果我想使用索引products.date。我對學習如何在沒有ORDER BY的情況下進行排序非常感興趣。到目前爲止,我無法弄清楚。 – jela 2011-12-22 18:18:52

+0

我也很好奇爲什麼'ORDER BY product_id'也會導致瓶頸,即使這並不涉及'products.date> ='comparison – jela 2011-12-22 18:35:35

+0

不幸的是,該查詢的運行速度與原始速度相同,大約爲3.5秒。我在產品表上創建了一個索引('shop_id,date')。也許需要其他一些索引? – jela 2011-12-23 01:30:16