This is the query that I am using on tables : products
, reviews
, replies
, review_images
.
Query :
SELECT products.id, reviews.*,
GROUP_CONCAT(DISTINCT CONCAT_WS('~',replies.reply, replies.time)) AS Replies,
GROUP_CONCAT(DISTINCT CONCAT_WS('~',review_images.image_title, review_images.image_location)) AS ReviewImages
FROM products
LEFT JOIN reviews on products.id = reviews.product_id
LEFT JOIN replies on reviews.id = replies.review_id
LEFT JOIN review_images on reviews.id = review_images.review_id
WHERE products.id = 1
GROUP BY products.id, reviews.id;
Schema :
Products :
id | name | product_details....
Reviews :
id | product_id | username | review | time | ...
Replies :
id | review_id | username | reply | time | ...
Review Images :
id | review_id | image_title | image_location | ...
Indexes:
Products :
PRIMARY KEY - id
Reviews :
PRIMARY KEY - id
FOREIGN KEY - product_id (id IN products table)
FOREIGN KEY - username (username IN users table)
Replies :
PRIMARY KEY - id
FOREIGN KEY - review_id (id IN reviews table)
FOREIGN KEY - username (username IN users table)
Review Images :
PRIMARY KEY - id
FOREIGN KEY - review_id (id IN reviews table)
Explain Query :
id | select_type | table | type | possible_keys | rows | extra
1 | SIMPLE | products | index | null | 1 | Using index; Using temporary; Using filesort
1 | SIMPLE | reviews | ALL | product_id | 4 | Using where; Using join buffer (Block Nested Loop)
1 | SIMPLE | replies | ref | review_id | 1 | Null
1 | SIMPLE | review_images | ALL | review_id | 5 | Using where; Using join buffer (Block Nested Loop)
I don't know what is wrong here, that it needs to use filesort and create a temporary table?
Here are few Profiling results :
Opening Tables 140 µs
Init 139 µs
System Lock 34 µs
Optimizing 21 µs
Statistics 106 µs
Preparing 146 µs
Creating Tmp Table 13.6 ms
Sorting Result 27 µs
Executing 11 µs
Sending Data 11.6 ms
Creating Sort Index 1.4 ms
End 89 µs
Removing Tmp Table 8.9 ms
End 34 µs
Query End 25 µs
Closing Tables 66 µs
Freeing Items 41 µs
Removing Tmp Table 1.4 ms
Freeing Items 46 µs
Removing Tmp Table 1.2 ms
Freeing Items 203 µs
Cleaning Up 55 µs
As from the Explain and Profiling results, it is clear that temporary table is created to produce the results. How can I optimize this query to get similar results and better performance and avoid the creation of temporary table?
Help would be appreciated. Thanks in advance.
EDIT
Create Tables
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` varchar(100) NOT NULL,
`items` int(11) NOT NULL,
`price` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
CREATE TABLE `reviews` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`product_id` int(11) NOT NULL,
`review` text NOT NULL,
`time` datetime NOT NULL,
`ratings` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `product_id` (`product_id`),
KEY `username` (`username`)
) ENGINE=InnoDB
CREATE TABLE `replies` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`review_id` int(11) NOT NULL,
`username` varchar(30) NOT NULL,
`reply` text NOT NULL,
`time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `review_id` (`review_id`)
) ENGINE=InnoDB
CREATE TABLE `review_images` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`review_id` int(11) NOT NULL,
`image_title` text NOT NULL,
`image_location` text NOT NULL,
PRIMARY KEY (`id`),
KEY `review_id` (`review_id`)
) ENGINE=InnoDB
EDIT:
I simplified the query above and now it does not create temporary tables. The only reason as mentioned by @Bill Karwin was that I was using GROUP BY
on second table in the joins.
Simplified query :
SELECT reviews. * ,
GROUP_CONCAT( DISTINCT CONCAT_WS( '~', replies.reply, replies.time ) ) AS Replies,
GROUP_CONCAT( DISTINCT CONCAT_WS( '~', review_images.image_title, review_images.image_location ) ) AS ReviewImages
FROM reviews
LEFT JOIN replies ON reviews.id = replies.review_id
LEFT JOIN review_images ON reviews.id = review_images.review_id
WHERE reviews.product_id = 1
GROUP BY reviews.id
Now the PROBLEM that I'm facing is :
Because I'm using GROUP_CONCAT, there is a limit to the data it can hold which is in the variable GROUP_CONCAT_MAX_LEN
, so as I'm concatenating the replies given by the users, it could go very very long and can possibly exceed the memory defined. I know I can change the value of GROUP_CONCAT_MAX_LEN
for current session, but still there is a limitation to it that at some point in time, the query may fail or unable to fetch complete results.
How can I modify my query so as not to use GROUP_CONCAT
and still get results expected.
POSSIBLE SOLUTION :
Simply using LEFT JOINS, which creates duplicate rows for every new result in the last column and which makes it hard to traverse in php
? Any suggestions?
I see this question is not getting enough response from SO members. But I've been looking for the solution and searching about concepts since last to last week. Still no luck. Hope some of you PROs can help me out. Thanks in advance.