Have the MySQL SELECT query below that is awfully slow.
It takes ~1.0 seconds to execute despite having only 300K rows and indexes, so I would love to find a way to get it to execute faster since it's a query that needs to be run again and again.
The query:
SELECT p.id, p.image, c.name, s.name, MIN(p.saleprice)
FROM products p
JOIN shops s ON p.shopid = s.id
JOIN products_category pc ON p.id = pc.product_id
JOIN categories c ON pc.category_id = c.id
WHERE p.brand_id > 0
AND pc.category_id = 46
AND pc.active = 1
AND p.price > 0
AND p.saleprice > 0
AND p.saleprice < p.price
AND (p.last_seen > DATE_SUB(NOW(), INTERVAL 2 DAY))
GROUP BY p.image
The query returns 960 rows.
The table products has 300.000 rows and these columns + one index:
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(512) DEFAULT NULL,
`shopid` int(11) DEFAULT NULL,
`image` varchar(512) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
`saleprice` int(11) DEFAULT NULL,
`last_seen` datetime DEFAULT NULL,
`brand_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `brand_id etc` (`brand_id`,`shopid`,`last_seen`,`price`,`saleprice`)
) ENGINE=InnoDB AUTO_INCREMENT=391671 DEFAULT CHARSET=utf8
The table products_categories also has 300.000 rows and these columns + two indexes:
CREATE TABLE `products_category` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) DEFAULT NULL,
`category_id` int(11) DEFAULT NULL,
`active` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `category_id etc` (`category_id`,`active`),
KEY `product_id etc` (`product_id`,`active`)
) ENGINE=InnoDB AUTO_INCREMENT=373364 DEFAULT CHARSET=utf8
The shops table. It has 15 rows and these columns:
CREATE TABLE `shops` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) DEFAULT NULL,
`active` int(11) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=56 DEFAULT CHARSET=utf8
Based on similar questions here, I have tried nesting things with an inner select:
SELECT p.id, p.image, c.name, s.name, MIN(p.saleprice)
FROM
(SELECT * FROM products WHERE p.brand_id > 0 AND price > 0 AND saleprice > 0 AND saleprice < price AND (p.last_seen > DATE_SUB(NOW(), INTERVAL 3 DAY))) p
JOIN shops s ON p.shopid = s.id
JOIN products_category pc ON p.id = pc.product_id
JOIN categories c ON pc.category_id = c.id
WHERE pc.category_id = 46
AND pc.active = 1
GROUP BY p.image
It didn't help. The version with the inner select takes ~1,3 seconds to execute.
The problem seems to be the join between products and products_category, i.e. the two big tables with 300K rows each.
The GROUP BY p.image is necessary (some products are just different sizes, so we use p.image to filter these out).
Maybe there's a trick I can do with my indexes? Or can any of you spot something else I should optimize?
EXPLAIN of the query:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE c \N const PRIMARY PRIMARY 4 const 1 100.00 Using temporary; Using filesort
1 SIMPLE pc \N ref category_id etc,product_id etc category_id etc 10 const,const 43104 100.00 Using where
1 SIMPLE p \N eq_ref PRIMARY,brand_id etc PRIMARY 4 pc.product_id 1 5.00 Using where
1 SIMPLE s \N eq_ref PRIMARY PRIMARY 4 p.shopid 1 100.00 \N