I have a query that takes 10-20 seconds, but I am sure it can be optimized, I am just not good enough to do it. I would like some help and explaining so that I can apply it to similar queries. Here is my query :
SELECT
`store_formats`.`Store Nbr`,
`store_formats`.`Store Name`,
`store_formats`.`Format Name`,
`eds_sales`.`Date`,
sum(`eds_sales`.`EPOS Sales`) AS Sales,
sum(`eds_sales`.`EPOS Quantity`) AS Quantity
FROM
`eds_sales`
INNER JOIN `item_codes` ON `eds_sales`.`Prime Item Nbr` = `item_codes`.`Customer Item`
INNER JOIN `store_formats` ON `eds_sales`.`Store Nbr` = `store_formats`.`Store Nbr`
WHERE
`eds_sales`.`Store Nbr` IN ($storenbr) AND
`eds_sales`.`Date` BETWEEN '$startdate' AND '$enddate' AND
`eds_sales`.`Client` = '$customer' AND
`eds_sales`.`Retailer` IN ($retailer) AND
`store_formats`.`Format Name` IN ($storeformat) AND
`item_codes`.`Item Number` IN ($products)
GROUP BY
`store_formats`.`Store Name`,
`store_formats`.`Store Nbr`,
`store_formats`.`Format Name`,
`eds_sales`.`Date`
Here is the explain output :

As you will see there, I have tried and created a few indexes with the columns involved with not much success. The main delay is caused from the copying to temporary table I think.
Those are the tables involved :
store_formats :
CREATE TABLE `store_formats` (
`id` int(12) NOT NULL,
`Store Nbr` smallint(5) UNSIGNED DEFAULT NULL,
`Store Name` varchar(27) DEFAULT NULL,
`City` varchar(19) DEFAULT NULL,
`Post Code` varchar(9) DEFAULT NULL,
`Region #` int(2) DEFAULT NULL,
`Region Name` varchar(10) DEFAULT NULL,
`Distr #` int(3) DEFAULT NULL,
`Dist Name` varchar(26) DEFAULT NULL,
`Square Footage` varchar(7) DEFAULT NULL,
`Format` int(1) DEFAULT NULL,
`Format Name` varchar(23) DEFAULT NULL,
`Store Type` varchar(20) DEFAULT NULL,
`TV Region` varchar(12) DEFAULT NULL,
`Pharmacy` varchar(3) DEFAULT NULL,
`Optician` varchar(3) DEFAULT NULL,
`Home Shopping` varchar(3) DEFAULT NULL,
`Retailer` varchar(15) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `store_formats`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `uniqness` (`Store Nbr`,`Store Name`,`Format`),
ADD KEY `Store Nbr_2` (`Store Nbr`,`Format Name`,`Store Name`);
eds_sales :
CREATE TABLE `eds_sales` (
`id` int(12) UNSIGNED NOT NULL,
`Prime Item Nbr` mediumint(7) NOT NULL,
`Prime Item Desc` varchar(255) NOT NULL,
`Prime Size Desc` varchar(255) NOT NULL,
`Variety` varchar(255) NOT NULL,
`WHPK Qty` int(5) NOT NULL,
`SUPPK Qty` int(5) NOT NULL,
`Depot Nbr` int(5) NOT NULL,
`Depot Name` varchar(50) NOT NULL,
`Store Nbr` smallint(5) UNSIGNED NOT NULL,
`Store Name` varchar(255) NOT NULL,
`EPOS Quantity` smallint(3) NOT NULL,
`EPOS Sales` decimal(13,2) NOT NULL,
`Date` date NOT NULL,
`Client` varchar(10) NOT NULL,
`Retailer` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `eds_sales`
ADD UNIQUE KEY `uniqness` (`Prime Item Nbr`,`Prime Item Desc`,`Prime Size Desc`,`Variety`,`WHPK Qty`,`SUPPK Qty`,`Depot Nbr`,`Depot Name`,`Store Nbr`,`Store Name`,`Date`,`Client`) USING BTREE,
ADD KEY `Store Nbr` (`Store Nbr`),
ADD KEY `Prime Item Nbr_2` (`Prime Item Nbr`,`Date`),
ADD KEY `id` (`id`) USING BTREE,
ADD KEY `Store Nbr_2` (`Prime Item Nbr`,`Store Nbr`,`Date`,`Client`,`Retailer`) USING BTREE,
ADD KEY `Client` (`Client`,`Store Nbr`,`Date`),
ADD KEY `Date` (`Date`,`Client`,`Retailer`);
item_codes :
CREATE TABLE `item_codes` (
`id` int(12) NOT NULL,
`Item Number` varchar(30) CHARACTER SET latin1 NOT NULL,
`Customer Item` mediumint(7) NOT NULL,
`Description` varchar(255) CHARACTER SET latin1 NOT NULL,
`Status` varchar(15) CHARACTER SET latin1 NOT NULL,
`Customer` varchar(30) CHARACTER SET latin1 NOT NULL,
`Sort Name` varchar(255) CHARACTER SET latin1 NOT NULL,
`EquidataCustomer` varchar(30) CHARACTER SET latin1 NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `item_codes`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `uniq` (`Item Number`,`Customer Item`,`Customer`,`EquidataCustomer`),
ADD KEY `Item Number_2` (`Item Number`,`Sort Name`,`EquidataCustomer`),
ADD KEY `Customer Item` (`Customer Item`,`Item Number`,`Sort Name`,`EquidataCustomer`),
ADD KEY `Customer Item_2` (`Customer Item`,`Item Number`,`EquidataCustomer`);
So my questions : As you can see I am joining 3 tables, and I am looking for sales by date by store format. I have been trying different kind of joins, or for example instead of joining sales to item_codes and store_formats, joining store_formats to the others, but with same results. I am also passing some arrays of variables using the IN as those are fed by select-boxes in the application.
UPDATE : Updated my tables with some suggestions from the comments
UPDATE : Modified my my.cnf as bellow increased performance (My RAM is 8GB, 2 cores, /data/tmp is on a 8 drive raid , same as where the data is)
tmpdir = /dev/shm/:/data/tmp:/tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
expire_logs_days = 10
max_binlog_size = 100M
innodb_buffer_pool_size = 6G
innodb_buffer_pool_instances = 6
query_cache_type=1
(Far too much to put in a Comment; please excuse me for using an Answer.)
When you have INDEX(a) and INDEX(a,b), the former is redundant and should be removed. I see about 5 cases of such.
Each store_nbr has exactly one store_name? If so, it is redundant to have store_name in more than one table. I don't know the intent of store_formats, but I guess that is the one table to house the store_name. Note that there is an inconsistent size for the datatypes of the two store_name columns and for the store_nbr columns!
Seems like every store should have a unique number, if so, then ADD UNIQUE KEY uniqness (Store Nbr,Store Name) should probably be turned into PRIMARY KEY(store_nbr). (Sorry, I am not going to put spaces in you column names.)
It is rarely useful to start an index with a date, so get rid of KEY Date_2 (Date,Client). In its place, add INDEX(Client, store_nbr, Date); that should have a direct impact on the speed of the query. You will probably see the EXPLAIN SELECT... change.
int(4) -- perhaps you meant SMALLINT UNSIGNED?
Having Date in a UNIQUE (or PRIMARY) key is usually "wrong". What it a 'Client' made two purchases of the same thing in the same day?
After you have made those changes, let's talk some more.
For consistency of viewing, please provide SHOW CREATE TABLE.
Avoid this construct:
FROM ( SELECT ... )
JOIN ( SELECT ... ) ON ...
It is inefficient because neither subquery has an index for making the JOIN efficient.
Moved selects into sub-queries to minimize items for the join. I believe MySQL would have done that already for you. I would check the execution plan for that info.
SELECT
stores.nbr, stores.name, stores.format,
epos.date,
sum(epos.sales) AS Sales,
sum(epos.qty) AS Quantity
FROM
(SELECT `Date` as `date`, `EPOS Sales` as sales,`EPOS Quantity` as qty, `Prime Item Nbr` as item_number, `Store Nbr` as store_number
FROM
`eds_sales`
WHERE
`eds_sales`.`Store Nbr` IN ($storenbr) AND
`eds_sales`.`Date` BETWEEN '$startdate' AND '$enddate' AND
`eds_sales`.`Client` = '$customer' AND
`eds_sales`.`Retailer` IN ($retailer)) as epos
INNER JOIN
(SELECT `Customer Item` as custItem
FROM `item_codes`
WHERE
`item_codes`.`Item Number` IN ($products)) as items ON epos.item_number = items.custItem
INNER JOIN
(SELECT `Store Nbr` as nbr, `Store Name` as name, `Format Name` as format
FROM
`store_formats`
WHERE
`store_formats`.`Format Name` IN ($storeformat)) as stores ON epos.store_number = stores.nbr
GROUP BY
stores.name,
stores.nbr,
stores.format,
epos.date
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With