Question :
OK here is my situation,
I’ve a ‘products’ table that contains my ‘parent’ products info.
And a ‘product_prices’ table that contains the parent product id and its full info
The website has different pages types to display products, one of them is ‘landing pages’ that contains custom products with main title for that landing page
and the last table here is the ‘landing_page_products’ that contains all parent
products ids
Here is the full structure for the 4 tables:
--
-- Table structure for table `landing_pages`
--
CREATE TABLE `landing_pages` (
`id` int(10) UNSIGNED NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `landing_page_products`
--
CREATE TABLE `landing_page_products` (
`id` int(10) UNSIGNED NOT NULL,
`landing_page_id` int(10) UNSIGNED NOT NULL DEFAULT '1',
`product_id` int(10) UNSIGNED NOT NULL DEFAULT '1',
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `products`
--
CREATE TABLE `products` (
`id` int(10) UNSIGNED NOT NULL,
`store_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`category_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`brand_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`lowest_price` double NOT NULL,
`highest_price` double NOT NULL,
`store_product_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`store_parent_product_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`fast_shipping` tinyint(1) NOT NULL,
`label` varchar(255) COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `product_prices`
--
CREATE TABLE `product_prices` (
`id` int(10) UNSIGNED NOT NULL,
`product_id` int(10) UNSIGNED NOT NULL DEFAULT '0',
`color` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`size` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`price` double(8,2) NOT NULL,
`discount_value` double(8,2) NOT NULL,
`discount_percentage` double(8,2) NOT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`sale_price` double NOT NULL,
`other_options` text COLLATE utf8_unicode_ci NOT NULL,
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`store_product_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`keywords` text COLLATE utf8_unicode_ci NOT NULL,
`image` text COLLATE utf8_unicode_ci NOT NULL,
`images` text COLLATE utf8_unicode_ci NOT NULL,
`weight` double(8,2) NOT NULL,
`upc` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`product_url` text COLLATE utf8_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `landing_pages`
--
ALTER TABLE `landing_pages`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `landing_page_products`
--
ALTER TABLE `landing_page_products`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `products`
--
ALTER TABLE `products`
ADD PRIMARY KEY (`id`);
--
-- Indexes for table `product_prices`
--
ALTER TABLE `product_prices`
ADD PRIMARY KEY (`id`);
--
-- AUTO_INCREMENT for dumped tables
--
--
-- AUTO_INCREMENT for table `landing_pages`
--
ALTER TABLE `landing_pages`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=67;
--
-- AUTO_INCREMENT for table `landing_page_products`
--
ALTER TABLE `landing_page_products`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=22052;
--
-- AUTO_INCREMENT for table `products`
--
ALTER TABLE `products`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=48904;
--
-- AUTO_INCREMENT for table `product_prices`
--
ALTER TABLE `product_prices`
MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=248270;
Total records in each table:
landing_pages: 66
landing_page_products: 22050 “22K”
products: 48903 “49K”
product_prices: 248269 “248K”
When i run the following query, it takes about 7-8 seconds
select `p`.`id`, `pp`.`title`, `pp`.`price`, `pp`.`sale_price`, `pp`.`discount_percentage`, `pp`.`image`,
(SELECT MIN(p2.sale_price) FROM products as p2 WHERE p.id = p2.product_id) as lowest_price,
(SELECT MAX(p3.sale_price) FROM products as p3 WHERE p.id = p3.product_id) as highest_price
from `products` as `p`
left join `product_prices` as `pp` on `p`.`id` = `pp`.`product_id`
where `p`.`id` in (select `pt`.`product_id` from `landing_page_products` as `pt` where `pt`.`landing_page_id` in (18))
group by `p`.`id`
order by `p`.`id` desc
limit 28 offset 0
Am i missing something here that makes that query too slow?
I also thought to combine the ‘products’ table with ‘product_prices’ table in one table to not use joins.
I’m using laravel 5.3 and PHP 7.0
Edit
I’ve the following foreign keys;
Table: landing_page_products
product_id
landing_page_id
Table: products
category_id
brand_id
Table: product_prices
product_id
Answer :
You need to add indexes on the fields used in joins or in where conditions.
You can run explain
on your query to see what indexes are used and where there is no index.
In your case, it seems that indexes on landing_page_id and parent_id will help a lot
The problem seems not the join but the improper use of subselect and INn clause
You query could be refactored as
select p.id, pp.title, pp.price, pp.sale_price, pp.discount_percentage, pp.image, t.lowest_price, t.highest_price
from products as p
inner join (
select id, min(sale_price) as lowest_price, max(sale_price) as highest_price
from products
group by id
) t on t.id = p.id
inner join (
select pt.product_id
from landing_page_products as pt
where pt.landing_page_id =18
) t2 on p.id = t2.product_id
left join product_prices as pp on p.id = pp.product_id
once you removed the unuseful subquery and join clause you then you can evaluate a proper indexing of your tables
and index on table product_price column product_id
CREATE INDEX my_product_index ON product_price (product_id )
and landing_page
CREATE INDEX my_landing_index ON landing_page(landing_page__id )