MySQL - 优化ORDER BY引起的“使用临时”

时间:2011-11-15 17:55:09

标签: mysql query-optimization

这是查询

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

3 个答案:

答案 0 :(得分: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;

这应该消除使用临时和使用filesort的额外步骤。如果没有,请尝试取消注释STRAIGHT_JOIN。

答案 1 :(得分:1)

所有其他明显不起作用的东西,我会提供以下内容...将产品评论移动到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;

这样,它应该以您期望的单行开始内部查询,然后加入评级操作。产品评论FIRST,获取相关产品的合格条目,然后继续加入评级评级行动的评级表...

答案 2 :(得分:0)

似乎其中一个表包含TEXT或BLOB字段,并且ORDER BY被强制使用磁盘进行排序。解决方案可能是在没有子查询中的字段的情况下进行排序,并再次与其余列连接。