以下方案需要哪些索引

时间:2017-06-06 11:31:25

标签: mysql sql indexing

我有一张包含一次性优惠券代码库存的表格。每行都是一个单独的代码。为简单起见,表格如下所示:

| id (AUTO_INCREMENTING) | owner (VARCHAR) | brand (VARCHAR) | currency (CHAR) | value (INTEGER) | activated_at (TIMESTAMP) | expires_at (DATE) |
|------------------------|-----------------|-----------------|-----------------|-----------------|--------------------------|-------------------|
| 1                      | first-owner     | brand-one       | gbp             | 10              | NULL                     | 2018-06-06        |
| 2                      | second-owner    | brand-one       | gbp             | 15              | NULL                     | 2018-06-06        |
| 3                      | second-owner    | brand-one       | gbp             | 15              | NULL                     | 2018-06-06        |

我知道我可以对表进行规范化并将所有字段提取到自己的表中,并使stock表只包含ids,但到目前为止还没有证明可以提供性能提升,为了简单和用户可读性,我倾向于保持原样。

查询此表时,我希望能够选择性地传递所有者品牌或两者。

对于以下查询,我需要在此表上使用哪些索引?为什么

在不提供所有者品牌的情况下查询表格:

SELECT
    `owner`,
    `brand`,
    `currency`,
    `value`,
    count(*) AS 'quantity_of_codes',
    (count(*) * `value`) AS 'total_face_value'
FROM `inventory`
WHERE `activated_at` IS NULL
AND `expires_at` >= '2018-06-05'
GROUP BY `owner`, `brand`, `currency`, `value`

如果我运行explain,我可以看到使用了索引:

| select_type | table     | type | possible_keys | key | key_len | ref   | rows   | Extra                                                     |
|-------------|-----------|------|---------------|-----|---------|-------|--------|-----------------------------------------------------------|
| SIMPLE      | inventory | ref  |               |     | 766     | const | 416144 | Using where; Using index; Using temporary; Using filesort |

当我提供所有者时,会使用索引,但它会创建一个临时表并使用 filesort ,我认为这不好。

SELECT
    `brand`,
    `owner`,
    `currency`,
    `value`,
    count(*) AS 'quantity_of_codes',
    (count(*) * `value`) AS 'total_face_value'
FROM `inventory`
WHERE `owner` = 'first-owner'
AND `activated_at` IS NULL
AND `expires_at` >= '2017-06-06'
GROUP BY `brand`, `owner`, `currency`, `value`

正在运行explain显示:

| select_type | table     | type  | possible_keys                                            | key                                                      | key_len | ref | rows | Extra                                                     |
|-------------|-----------|-------|----------------------------------------------------------|----------------------------------------------------------|---------|-----|------|-----------------------------------------------------------|
| SIMPLE      | inventory | index | brand_owner_currency_value_activated_at_expires_at_index | brand_owner_currency_value_activated_at_expires_at_index | 2310    |     | 87   | Using where; Using index; Using temporary; Using filesort |

当我还提供品牌时,我得到:

SELECT
    `brand`,
    `owner`,
    `currency`,
    `value`,
    count(*) AS 'quantity_of_codes',
    (count(*) * `value`) AS 'total_face_value'
FROM `inventory`
WHERE `owner` = 'first-owner'
AND `brand` = 'brand-one'
AND `activated_at` IS NULL
AND `expires_at` >= '2018-06-05'
GROUP BY `brand`, `owner`, `currency`, `value`

最后,当我运行explain时,我得到了:

| select_type | table     | type | possible_keys                                            | key                                                      | key_len | ref | rows | Extra                                                     |
|-------------|-----------|------|----------------------------------------------------------|----------------------------------------------------------|---------|-----|------|-----------------------------------------------------------|
| SIMPLE      | inventory | ref  | brand_owner_currency_value_activated_at_expires_at_index | brand_owner_currency_value_activated_at_expires_at_index | 1532    |     | 1    | Using where; Using index; Using temporary; Using filesort |

如上所述,我有品牌所有者货币 activated_at 和 expires_at 字段,但如果我在查询的where部分中不包含品牌,则不会使用此字段。

2 个答案:

答案 0 :(得分:2)

http://use-the-index-luke.com/是一个很好的参考,可以让你巧妙地设计你的索引。

通过为此类查询选择复合索引,您处于正确的轨道上。您的查询包含这些WHERE - 子句项。

 WHERE `owner` = 'an-owner'
   AND `activated_at` IS NULL
   AND `expires_at` >= '2017-06-06'

您正在寻找第一项上的相等性,第二项上的IS NULL和第三项上的范围扫描。因此,(owner, activated_at, expires_at)上的索引可以允许查询计划程序跳转到正确的行,然后扫描索引以获取连续的行。 (activated_at, owner, expires_at)上的索引也可以使用。

对于您显示的查询,无法对( brand, owner, currency, value, _activated_at, _expires_at)上的索引进行范围扫描。所以查询规划器会忽略它。

专业提示:不要为表创建索引,因为它们似乎有用。设计它们以加快您需要加速的查询。

专业提示#2:使用临时;使用filesort 并不是表现不佳的明显标志。 temporary 意味着需要将一些结果集放入服务器中的短暂的表格式数据结构中。 filesort 表示结果集在交付前需要排序。使用GROUP BY时,查询计划程序通常需要临时结构来完成查询。读这个。 https://www.percona.com/blog/2009/03/05/what-does-using-filesort-mean-in-mysql/

您的EXPLAIN输出对我来说非常好看。一排?太好了,

答案 1 :(得分:0)

我会把它写成:

SELECT brand, owner, currency, value,
       count(*) AS quantity_of_codes, sum(value) AS total_face_value
FROM inventory i
WHERE owner = 'an-owner' AND
      activated_at IS NULL AND 
      expires_at >= '2017-06-06'
GROUP BY brand, owner, currency, value;

对于此查询,最佳索引为inventory(owner, activated_at, expires_at)。按此顺序排列的这些列可用于WHERE子句。在MySQL中,你对GROUP BY

的了解并不多