使用2个联接和GROUPBY子句优化mysql查询

使用2个联接和GROUPBY子句优化mysql查询,mysql,indexing,query-optimization,mariadb,temp-tables,Mysql,Indexing,Query Optimization,Mariadb,Temp Tables,我有一个需要10-20秒的查询,但我确信它可以被优化,我只是不够好去做。我希望得到一些帮助和解释,以便我可以将其应用于类似的查询。 我的问题是: SELECT `store_formats`.`Store Nbr`, `store_formats`.`Store Name`, `store_formats`.`Format Name`, `eds_sales`.`Date`, sum(`eds_sales`.`EP

我有一个需要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`
以下是解释输出:

正如您将在这里看到的,我已经尝试并创建了一些包含列的索引,但没有多大成功。我认为主要的延迟是由于复制到临时表造成的

涉及的表格如下:

存储单元格式:

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_销售:

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`);
项目代码:

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_代码和store_格式,而是将store_格式连接到其他格式,但结果相同。我还使用IN传递一些变量数组,因为这些变量是由应用程序中的选择框提供的

加入这些表的最佳方式 建议每个表的最佳索引 为什么我需要临时桌子?是不是因为小组的原因?有解决办法吗? 如果需要临时表,是否有加快创建速度的方法?我已经在一个有8个磁盘的raid中设置了数据文件夹,但速度仍然很慢。 当然,任何建议的替代方案都是受欢迎的 更新:用评论中的一些建议更新了我的表格

更新:将my.cnf修改为bellow提高了性能我的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

将选择移动到子查询中,以最小化联接的项。我相信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

太多太多的评论;请原谅我使用了答案

当有INDEXa和INDEXa,b时,前者是多余的,应该删除。我看到大约5例这样的病例

每个门店都有一个门店名称?如果是这样的话,在多个表中有store_名称是多余的。我不知道store_格式的用意,但我想这是一个存放store_名称的表。请注意,两个store_name列和store_nbr列的数据类型大小不一致

似乎每个存储都应该有一个唯一的编号,如果是这样,那么添加唯一的密钥uniqness store Nbr,存储名称可能应该变成PRIMARY KEYstore_Nbr。对不起,我不会在您的列名中加空格

以日期开始索引很少有用,所以请去掉键date\u 2 date,Client。在其位置添加INDEXClient、store_nbr、Date;这将直接影响查询的速度。您可能会看到解释选择。。。改变

int4-也许你是说SMALLINT未签名

在唯一或主键中包含日期通常是错误的。“客户”在同一天购买了两件相同的东西,这意味着什么

在您完成这些更改之后,让我们再谈一些

为了查看的一致性,请提供“显示创建表”

避免这种构造:

FROM ( SELECT ... )
JOIN ( SELECT ... ) ON ...

这是低效的,因为两个子查询都没有使联接高效的索引。

将联接表上的条件从WHERE子句移动到联接的on子句:

创建以下索引:

CREATE INDEX IDX001 ON eds_sales (Client,`Store Nbr`,`Retailer`,`Date`);
CREATE INDEX IDX002 ON store_formats (`Store Nbr`,`Format Name`);

如果有效,请告诉我,我会解释原因。

这个问题可能会得到更多专家的关注。请您将问题显示在表上的所有索引,好吗?在问题中添加了索引。出于爱心,请不要在表/列标识符中使用空格。在有大量记录的表中,尽量减少varchar长度,我想你的情况可能不需要255。删除所有索引并仅创建join/WHERE/GROUP/ORDER BY所需的索引,还可以尝试使用外键,强制数据完整性和相同的格式/长度。为了进行测试,我会在查询中添加一些常量,比如1,2,3中的WHERE somecolumn,然后用一个具体的条件替换它。我看到查询使用的是索引,但可能有很多冗余,有时它们可能不适合RAM内存。谢谢第一点,我不确定它是如何工作的。对于重复的列,这些列来自excel文件,直接导入mysql。我计划在将来删除它们,但现在它们在一些快速查询中有一些用途。对于uniqness,如果我将这个唯一键转换为primary,它仍然是唯一的吗?用日期更改了索引。修改数据类型以匹配所有表。对于unique中的日期,它需要是这样的,因为我不是在处理客户,而是针对商店的数据,所以uniqness是在storenbr的日期上定义的,其他我会将我的show create table和performance发布到Morow。谢谢主键是唯一键是索引。
CREATE INDEX IDX001 ON eds_sales (Client,`Store Nbr`,`Retailer`,`Date`);
CREATE INDEX IDX002 ON store_formats (`Store Nbr`,`Format Name`);