【问题标题】:mysql query using the wrong indexesmysql查询使用错误的索引
【发布时间】:2016-11-26 02:25:38
【问题描述】:

我在 mysql 数据库中的一些查询存在一些优化问题。构建应用程序后,我尝试使用 mysqltuner 进行优化并进行解释,以查找非索引查询。这是一个经常运行并报告未使用索引的查询:

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`);

【问题讨论】:

  • 在您看来,为什么该查询必须使用索引?您认为如果查询使用索引,它会使其更快吗?你有没有想过不能使用索引的查询?看看你一开始就有的SELECT COUNT(*)。它翻译为“计算这里的一切”。为了计算 everything,您需要获取 everything 并且为此您不需要索引。您根本不必优化该查询。您可以简单地每天运行几次并将计数保存在某处,这样您就不会在每次需要计数时都敲击数据库。
  • 认为日期范围是来自 php 的变量,对于进入 store_formats.Format Name IN ('format1','format2') 的商店格式数组也是如此因此,为了存储结果,我需要运行成千上万的组合
  • 我不是告诉人们如何处理他们的项目,但是当您创建数据库模型时,您通常会考虑您将如何处理数据以及数据的用途.您的查询非常丑陋且难以阅读。创建复杂的模型和查询并不难——很容易。困难的是创建适用于各种场景的简单东西。您设计它的方式不会让自己被轻松使用或查询。但是,正如我所提到的 - 你最初的问题是为什么 MySQL 不使用索引,而答案是 - 因为它不需要。
  • 它正在使用索引,但选择了错误的索引,这是我的问题:可能的键:uniqness,Prime Item Nbr,Store Nbr - Used Key:uniqness。这可能不是这里的全部问题,我理解,但由于它已被标记,我想解决它
  • 至少对我来说,链接不起作用(这就是为什么重要信息不应该只在外部链接中提供),所以我无法检查您拥有和使用哪些索引,但@987654332 @ 和eds_sales(Prime Item Nbr,Store Nbr) 似乎是首选。 count(*) 不会“计算所有内容”,它会计算子查询中的行数,它可以并且应该使用索引。你能解释一下最后一个IN 查询(store_formats.Store Name IN... 部分)吗?乍一看,好像您可以用这个子查询(在IN 中)替换整个查询,然后用所有store_formats 替换join

标签: mysql indexing mariadb explain mysqltuner


【解决方案1】:

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

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

错误??你真的想要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 ...

在您清理完大部分内容后,如果仍有问题,我们可以回复您关于“错误索引”的问题。 (如果您需要进一步的帮助,请开始一个新问题。)

【讨论】:

  • 我已经看到使用上述建议的 10 倍改进。最重要的是创建新索引。所以基本上正确使用索引是为查询中使用的所有项目创建一个组合索引。因此,如果我要查询 date = something 的 id,我需要 id 和日期的索引。我还使用 PROCEDURE ANALYSE() 的建议修改了所有结构。但是有一个问题,虽然有 1.000.000 列使用 1,0 而不是布尔值(或 tinyint(0) 它建议 ENUM('0','1') ,哪一个是正确的?
  • 我的意思是 tinyint(1)
  • TINYINT UNSIGNED - 是的。或者ENUM('male', 'female'),等等。“合并所有项目”——不完全;见Index Cookbook
  • 该规则更像是一些“=”在前,一个范围(例如 DATE BETWEEN)在后。
  • WHERE .. AND 中的订单无所谓;在INDEX(a,b) 中的顺序确实很重要。
猜你喜欢
  • 1970-01-01
  • 2013-07-10
  • 2016-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-22
相关资源
最近更新 更多