我有一张包含一次性优惠券代码库存的表格。每行都是一个单独的代码。为简单起见,表格如下所示:
| 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部分中不包含品牌,则不会使用此字段。
答案 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
。