2011-11-15 60 views
1

下面是該查詢MySQL的 - 優化「使用臨時」引發的ORDER BY

SELECT * FROM ProductReviews 
INNER JOIN RatingActions USING(RatingActionID) 
LEFT JOIN ProductRatingVotes USING(RatingActionID) 
WHERE ProductReviews.ProductID="200129" AND ProductReviewStatus="1" 
ORDER BY RatingActionTimestamp DESC; 

這裏是執行計劃

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: ProductReviews 
     type: ref 
possible_keys: FK_ProductReview_ProductID,FK_ProductReviews_RatingActionID 
      key: FK_ProductReview_ProductID 
     key_len: 4 
      ref: const 
     rows: 1 
     Extra: Using where; Using temporary; Using filesort 
*************************** 2. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: ProductRatingVotes 
     type: ref 
possible_keys: FK_ProductRatingVotes_RatingActionID 
      key: FK_ProductRatingVotes_RatingActionID 
     key_len: 4 
      ref: scart.ProductReviews.RatingActionID 
     rows: 1 
     Extra: 
*************************** 3. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: RatingActions 
     type: eq_ref 
possible_keys: PRIMARY 
      key: PRIMARY 
     key_len: 4 
      ref: scart.ProductReviews.RatingActionID 
     rows: 1 
     Extra: 
3 rows in set (0.00 sec) 

雖然它的掃描只有一個排,using temporary殺死它,它需要3-4秒才能完成(一個非常繁忙的服務器;在我的本地主機上0.004秒,與沒有排序的版本相比,仍然是6倍慢)。

據我所知,using temporary是由於order by列不在第一個表中。

有沒有一種方法來優化此查詢,或者我應該將Timestamp複製到ProductReviews表中?

UPDATE 表:

CREATE TABLE `ProductReviews` (
`ProductReviewID` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`ProductID` int(10) unsigned NOT NULL DEFAULT '0', 
`RatingActionID` int(10) unsigned NOT NULL DEFAULT '0', 
`ProductReviewText` text NOT NULL, 
`ProductReviewStatus` tinyint(3) unsigned NOT NULL DEFAULT '0', 
PRIMARY KEY (`ProductReviewID`), 
KEY `FK_ProductReview_ProductID` (`ProductID`), 
KEY `FK_ProductReviews_RatingActionID` (`RatingActionID`), 
CONSTRAINT `FK_ProductReviews_RatingActionID` FOREIGN KEY (`RatingActionID`) REFERENCES `ratingactions` (`RatingActionID`) ON DELETE CASCADE ON UPDATE CASCADE, 
CONSTRAINT `FK_ProductReview_ProductID` FOREIGN KEY (`ProductID`) REFERENCES `products` (`ProductID`) ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=66 DEFAULT CHARSET=utf8 

CREATE TABLE `ratingactions` (
`RatingActionID` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`RatingActionTimestamp` int(10) unsigned NOT NULL DEFAULT '0', 
`CustomerID` int(10) unsigned DEFAULT NULL, 
`RatingActionIPAddress` int(10) unsigned NOT NULL DEFAULT '0', 
`RatingActionInputName` varchar(255) NOT NULL DEFAULT '', 
PRIMARY KEY (`RatingActionID`), 
KEY `FK_RatingActions_CustomerID` (`CustomerID`), 
CONSTRAINT `FK_RatingActions_CustomerID` FOREIGN KEY (`CustomerID`) REFERENCES `customers` (`CustomerID`) ON DELETE SET NULL ON UPDATE CASCADE 
) ENGINE=InnoDB AUTO_INCREMENT=142 DEFAULT CHARSET=utf8 

CREATE TABLE `ProductRatingVotes` (
`ProductRatingVoteID` int(10) unsigned NOT NULL AUTO_INCREMENT, 
`ProductID` int(10) unsigned NOT NULL DEFAULT '0', 
`RatingActionID` int(10) unsigned NOT NULL DEFAULT '0', 
`ProductRatingVoteValue` tinyint(3) unsigned NOT NULL DEFAULT '0', 
`ProductRatingVoteStatus` tinyint(3) unsigned NOT NULL DEFAULT '0', 
PRIMARY KEY (`ProductRatingVoteID`), 
KEY `FK_ProductRatingVotes_ProductID` (`ProductID`), 
KEY `FK_ProductRatingVotes_RatingActionID` (`RatingActionID`), 
CONSTRAINT `FK_ProductRatingVotes_ProductID` FOREIGN KEY (`ProductID`) REFERENCES 
`products` (`ProductID`) ON DELETE CASCADE ON UPDATE CASCADE, 
CONSTRAINT `FK_ProductRatingVotes_RatingActionID` FOREIGN KEY 
(`RatingActionID`) REFERENCES `ratingactions` (`RatingActionID`) ON 
DELETE CASCADE ON UPDATE CASCADE) 
ENGINE=InnoDB AUTO_INCREMENT=142 
DEFAULT CHARSET=utf8 
+0

你確定你需要所有列作爲結果? – newtover

+0

將'*'改爲一個整數列 - 查詢速度稍快,但執行計劃沒有改變 –

+0

順便說一下,根據執行計劃,您的命令由IS在第一個表中執行。所以你說,'SELECT RatingActions.RatingActionID FROM ...'在生產中幾乎與'SELECT * ...'同時存在,不是嗎? – newtover

回答

0

看來,其中一個表包含文本或BLOB字段和ORDER BY被迫使用磁盤排序。一種解決方案可能是在沒有子查詢中的字段的情況下進行排序,並再次與其餘列進行連接。

+0

true,有一個文本列,ProductReviewText,但將*更改爲RatingActionID不影響執行計劃 –

1

試試這個:

SELECT /*STRAIGHT_JOIN*/ ProductReviews.ProductID, RatingActionTimestamp 
FROM ratingactions 
join ProductReviews USING(RatingActionID) 
LEFT JOIN ProductRatingVotes USING(RatingActionID) 
WHERE ProductReviews.ProductID=200129 AND ProductReviewStatus=1 
order by RatingActionTimestamp desc; 

這應該消除了使用臨時使用文件排序額外的步驟。如果沒有,請嘗試取消註釋STRAIGHT_JOIN。

+0

沒有STRAIGHT_JOIN - 執行計劃相同。使用STRAIGHT_JOIN - 刪除臨時文件,但整個RatingActions表正在被掃描(類型:ALL) –

1

與所有其他不起作用的明顯的東西,我會然後提供以下...將產品評論移動到第一個位置作爲一個select/from本身...然後應用連接

SELECT STRAIGHT_JOIN 
     PR.ProductID, 
     RA.RatingActionTimestamp 
    FROM 
     (select PR1.ProductID, 
       PR1.RatingActionID 
      FROM ProductReviews PR1 
      WHERE 
       PR1.ProductID = 200129 
      AND PR1.ProductReviewStatus = 1) PR 
     JOIN rating actions RA 
      on PR.RatingActionID = RA.RatingActionID 
     LEFT JOIN ProductRatingVotes PRV 
      on PR.RatingActionID = PRV.RatingActionID 
    order by 
     RA.RatingActionTimestamp desc; 

這樣,它應該與您的預期,加盟評級行動。產品條用戶,讓那些有資格的條目有問題的獨特產品的單排造成的內部查詢開始,然後繼續參加了到評級行動的評級和評級表...

相關問題