我的查询需要10-20秒,但我确信它可以优化,我只是不够好。我想要一些帮助和解释,以便我可以将它应用于类似的查询。 这是我的疑问:
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`
正如您将在那里看到的那样,我尝试并创建了一些索引,其中涉及的列没有太大成功。主要的延迟是由我认为复制到临时表引起的。
这些是涉及的表格:
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`);
所以我的问题: 正如您所看到的,我正在加入3个表,我正在按商店格式查找销售日期。我一直在尝试不同类型的连接,或者例如,不是将销售加入item_codes和store_formats,而是将store_formats连接到其他连接,但结果相同。我也使用IN传递一些变量数组,因为它们是由应用程序中的选择框提供的。
更新:使用评论中的一些建议更新了我的表格
更新:修改我的my.cnf,因为性能提升(我的RAM为8GB,2核,/ data / tmp在8驱动器raid上,与数据相同)
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
答案 0 :(得分:2)
(评论太多了;请原谅我使用答案。)
当您有INDEX(a)
和INDEX(a,b)
时,前者是多余的,应该删除。我看到了大约5个案例。
每个store_nbr
只有一个store_name
?如果是这样,在多个表中使用store_name
是多余的。我不知道store_formats
的意图,但我想这是容纳store_name
的一个表。请注意,两个store_name
列和store_nbr
列的数据类型的大小不一致!
似乎每家商店都应该有一个唯一的号码,如果是这样,那么ADD UNIQUE KEY uniqness
(Store Nbr
,Store Name
)应该变成PRIMARY KEY(store_nbr)
。 (对不起,我不打算在列名中添加空格。)
使用日期启动索引很少有用,因此请删除KEY Date_2
(Date
,Client
)。取而代之的是,添加INDEX(Client, store_nbr, Date)
;这应该对查询的速度有直接影响。您可能会看到EXPLAIN SELECT...
更改。
int(4)
- 也许你的意思是SMALLINT UNSIGNED
?
在Date
(或UNIQUE
)密钥中使用PRIMARY
通常是“错误的”。什么是“客户”在同一天购买了两件相同的东西?
完成这些更改后,我们再谈谈。
为了保持一致性,请提供SHOW CREATE TABLE
。
避免使用此构造:
FROM ( SELECT ... )
JOIN ( SELECT ... ) ON ...
这是低效的,因为子查询都没有使JOIN
有效的索引。
答案 1 :(得分:1)
将选择移动到子查询中以最小化连接的项目。我相信MySQL已经为你做过了。我会查看该信息的执行计划。
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
答案 2 :(得分:0)
将连接表上的条件从WHERE
子句移动到连接的ON
子句:
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`
JOIN `item_codes`
ON `eds_sales`.`Prime Item Nbr` = `item_codes`.`Customer Item`
AND `item_codes`.`Item Number` IN ($products)
JOIN `store_formats`
ON `eds_sales`.`Store Nbr` = `store_formats`.`Store Nbr`
AND `store_formats`.`Format Name` IN ($storeformat)
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)
GROUP BY
`store_formats`.`Store Name`,
`store_formats`.`Store Nbr`,
`store_formats`.`Format Name`,
`eds_sales`.`Date`
创建以下索引:
CREATE INDEX IDX001 ON eds_sales (Client,`Store Nbr`,`Retailer`,`Date`);
CREATE INDEX IDX002 ON store_formats (`Store Nbr`,`Format Name`);
如果有效,请告诉我,我会解释原因。