2016-07-22 86 views
1

我在我的一些mysql数据库查询中遇到了一些优化问题。在构建我的应用程序之后,我试图使用mysqltuner进行优化并解释,以查找非索引查询。这是一个经常运行的查询和不使用索引报告:使用错误索引的mysql查询

SELECT count(*) AS rangedandselling 
    FROM 
     (SELECT DISTINCT `store_formats`.`Store Name` 
      FROM (`eds_sales` 
        JOIN `store_formats` 
         ON (`eds_sales`.`Store Nbr` = `store_formats`.`Store Nbr`) 
       ) 
      WHERE `eds_sales`.`Prime Item Nbr` = '4' 
       AND `eds_sales`.`Date` BETWEEN CAST('2016-07-14' AS DATETIME) 
              AND CAST('2016-07-21' AS DATETIME) 
       AND `store_formats`.`Format Name` IN ('format1','format2') 
       AND `store_formats`.`Store Name` IN (
       SELECT DISTINCT `store_formats`.`Store Name` 
        FROM (`eds_stock` 
          JOIN `store_formats` 
           ON (`eds_stock`.`Store Nbr` = `store_formats`.`Store Nbr`) 
         ) 
        WHERE `eds_stock`.`Prime Item Nbr` = '4' 
         AND `eds_stock`.`Date` BETWEEN CAST('2016-07-14' AS DATETIME) 
                AND CAST('2016-07-21' AS DATETIME) 
         AND `store_formats`.`Format Name` IN ('format1','format2') 
         AND `eds_stock`.`Curr Traited Store/Item Comb.` = '1') 
    ) t 

这是解释输出:https://tools.mariadb.org/ea/pyb3h

虽然我已经收录参与加入和查找列,它看起来就像它正在选择另一个索引。这个其他索引被称为uniqness,并且由用于插入的源列中的6个不同的列组成(这些列的组合是使得行唯一的唯一因素,因此我给出了这个名称)。然后我确定我有其他列的索引,我可以在解释中看到它们。我不知道为什么会发生这种情况,有人可以帮忙吗?

关于优化此查询的任何想法?

这里是那些上面的链接不起作用的解释:

| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | 
+---+---+---+---+---+---+---+---+---+---+ 
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 167048 | | 
| 2 | DERIVED | eds_sales | ref | uniqness,Prime Item Nbr,Store Nbr | uniqness | 4 | const | 23864 | Using where; Using index; Using temporary | 
| 2 | DERIVED | store_formats | ref | Store Nbr,Store Name,Format Name | Store Nbr | 5 | equidata.eds_sales.Store Nbr | 1 | Using where | 
| 2 | DERIVED | <subquery3> | eq_ref | distinct_key | distinct_key | 84 | func | 1 | Distinct | 
| 3 | MATERIALIZED | store_formats | ALL | Store Nbr,Store Name,Format Name | NULL | NULL | NULL | 634 | Using where; Distinct | 
| 3 | MATERIALIZED | eds_stock | ref | uniqness,Prime Item Nbr,Store Nbr | uniqness | 8 | const,equidata.store_formats.Store Nbr | 7 | Using where; Distinct | 
+---+---+---+---+---+---+---+---+---+---+ 

我也张贴了相关的表结构:

-- 
-- Table structure for table `eds_sales` 
-- 
CREATE TABLE `eds_sales` (
    `id` int(12) NOT NULL, 
    `Prime Item Nbr` int(12) 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(255) NOT NULL, 
    `Store Nbr` int(5) NOT NULL, 
    `Store Name` varchar(255) NOT NULL, 
    `EPOS Quantity` int(5) NOT NULL, 
    `EPOS Sales` float(4,2) NOT NULL, 
    `Date` date NOT NULL, 
    `Client` varchar(255) NOT NULL, 
    `Retailer` varchar(255) NOT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=latin1; 

ALTER TABLE `eds_sales` 
    ADD PRIMARY KEY (`id`), 
    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 `Prime Item Nbr` (`Prime Item Nbr`), 
    ADD KEY `Store Nbr` (`Store Nbr`); 

表结构表eds_stock

CREATE TABLE `eds_stock` (
    `Prime Item Nbr` int(12) NOT NULL, 
    `Prime Item Desc` varchar(255) NOT NULL, 
    `Prime Size Desc` varchar(255) NOT NULL, 
    `Variety` varchar(255) NOT NULL, 
    `Curr Valid Store/Item Comb.` int(12) NOT NULL, 
    `Curr Traited Store/Item Comb.` int(12) NOT NULL, 
    `Store Nbr` int(12) NOT NULL, 
    `Store Name` varchar(255) NOT NULL, 
    `Curr Str On Hand Qty` int(12) NOT NULL, 
    `Curr Str In Transit Qty` int(12) NOT NULL, 
    `Curr Str On Order Qty` int(12) NOT NULL, 
    `Curr Str In Depot Qty` int(12) NOT NULL, 
    `Curr Instock %` int(12) NOT NULL, 
    `Max Shelf Qty` int(12) NOT NULL, 
    `On Hand Qty` int(12) NOT NULL, 
    `Date` date NOT NULL, 
    `Client` varchar(255) NOT NULL, 
    `Retailer` varchar(255) NOT NULL 
) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

ALTER TABLE `eds_stock` 
    ADD UNIQUE KEY `uniqness` (`Prime Item Nbr`,`Store Nbr`,`Date`,`Client`,`Retailer`), 
    ADD KEY `Prime Item Nbr` (`Prime Item Nbr`), 
    ADD KEY `Store Nbr` (`Store Nbr`), 
    ADD KEY `Curr Valid Store/Item Comb.` (`Curr Valid Store/Item Comb.`); 

表格结构store_formats

CREATE TABLE `store_formats` (
    `id` int(12) NOT NULL, 
    `Store Nbr` int(4) 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 KEY `Store Nbr` (`Store Nbr`), 
    ADD KEY `Store Name` (`Store Name`), 
    ADD KEY `Format Name` (`Format Name`); 
+2

而在您的心中,为什么该查询必须使用索引?你认为如果一个查询使用索引,它使它更快?你有没有想过不能使用索引的查询?查看一开始就有的“SELECT COUNT(*)”。它转化为“在这里计数一切”。为了cout * everything *,你需要获取* everything *,并且你不需要索引。根本没有必要优化该查询。您可以简单地每天运行几次,并将计数保存在某个地方,这样每次需要计数时都不会敲击db。 – Mjh

+0

认为日期范围是一个来自php的变量,对于进入'store_formats'.'Format Name' IN('format1','format2')的商店格式数组也是如此。因此为了存储结果I将需要运行数千和数千个组合 –

+1

这不是我的地方告诉人们如何处理他们的项目,但是当你创建数据库模型时,你通常会考虑你将如何处理数据和数据是为了。您的查询非常难看,难以阅读。创建复杂的模型和查询并不难 - 这很容易。什么是困难的是创造一些简单的东西,在任何情况下都有效。您设计的方式不会让自己轻易被使用或查询。然而,正如我所提到的 - 你最初的问题是为什么MySQL不使用索引,答案是 - 因为它不是必需的。 – Mjh

回答

1

CAST('2016-07-14' AS DATETIME) - 不需要CAST; '2016-07-14'工作正常。 (特别是因为您正在与日期进行比较)。

IN (SELECT ...)效率低下。更改为JOIN

eds_stock,而不是

INDEX(`Prime Item Nbr`) 

有两个:

INDEX(`Prime Item Nbr`, `Date`) 
INDEX(`Prime Item Nbr`, `Curr Traited Store/Item Comb.`, `Date`) 

INT始终是一个4字节的数字,即使你说int(2)。考虑切换到TINYINT UNSIGNED(和其他尺寸INT)。

float(4,2) - 请勿使用(m,n);它会导致额外的舍入并导致不希望的截断。要么使用DECIMAL(4,2)(为了钱),要么使用简单的FLOAT

Bug ??你真的要8天,而不仅仅是一个星期

AND `Date` BETWEEN CAST('2016-07-14' AS DATETIME) AND CAST('2016-07-21' AS DATETIME) 

我喜欢这种模式:

AND `Date` >= '2016-07-14' 
AND `Date` < '2016-07-14' + INTERVAL 1 WEEK 

而不是两个选择

SELECT count(*) AS rangedandselling 
    FROM (SELECT DISTINCT `store_formats`.`Store Name` ... 

一个选择可能会工作(并更快):

SELECT COUNT(DISTINCT `store_formats`.`Store Name`) AS rangedandselling ... 

一旦你已经清理了大部分内容,如果仍然存在问题,我们可以回到关于“错误索引”的问题。 (如果您需要进一步的帮助,请开始一个新的问题。)

+0

使用上述建议我已经看到了10倍的改进。那个碰到的地方就是创建新的索引。所以基本上正确使用索引是为查询中使用的所有项目创建一个组合索引。所以如果我在查询某个日期=某处的id,我需要一个id和日期的索引。我还使用PROCEDURE ANALYZE()的建议修改了所有结构。尽管有一个问题,尽管有1.000.000列,而1,0代替布尔值(或tinyint(0),它建议ENUM('0','1'),哪一个是正确的? –

+0

我的意思是tinyint(1) –

+0

'TINYINT UNSIGNED' - 是的,或者'ENUM('male','female')'等。“合并所有项目” - 不完全;参见[_Index Cookbook_](https://mariadb.com/kb/en/mariadb/building-the-best-index-for-a-select-select /) –