Reputation: 26699
Here is the query
SELECT * FROM ProductReviews
INNER JOIN RatingActions USING(RatingActionID)
LEFT JOIN ProductRatingVotes USING(RatingActionID)
WHERE ProductReviews.ProductID="200129" AND ProductReviewStatus="1"
ORDER BY RatingActionTimestamp DESC;
Here is the execution plan
*************************** 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)
Although it's scanning exactly one row, the using temporary
kills it and it takes 3-4 seconds to complete (a very busy server; on my localhost 0.004 seconds, which still is more than 6 times slower compared to version without order by).
As I understand, the using temporary
is caused by the fact, that the order by
column is not in the first table.
Is there a way to optimize this query, or should I duplicate the Timestamp into the ProductReviews table?
UPDATE Tables:
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
Upvotes: 1
Views: 1062
Reputation: 48139
with all the other apparent things that are not working, I would then offer the following... Move the Product Reviews to the FIRST POSITION in the from as a select/from of itself... THEN apply the joins
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;
This way, it should START with the inner query resulting with the single row you expect, THEN join to rating action .product reviews FIRST, get those qualified entries for the distinct product in question, THEN continue the join out to the ratings and rating votes tables on the rating actions...
Upvotes: 1
Reputation: 6724
Try this instead:
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;
This should eliminate the using temporary and using filesort extra steps. If it doesn't, try uncommenting the STRAIGHT_JOIN.
Upvotes: 1
Reputation: 32094
It seems that one of the tables contains TEXT or BLOB fields and the ORDER BY is forced to use disk for sorting. A solution might be to sort without the fields in a subquery and join with the rest of columns once again.
Upvotes: 0