如果我有一个像下面的语句那样的select语句,那么索引中应该包含哪些顺序和哪些列?
SELECT MIN(BenchmarkID),
MIN(BenchmarkDateTime),
Currency1,
Currency2,
BenchmarkType
FROM Benchmark
INNER JOIN MyCurrencyPairs ON Currency1 = Pair1
AND Currency2 = Pair2
WHERE BenchmarkDateTime > IN_BeginningTime
GROUP BY Currency1, Currency2, BenchmarkType;
需要注意的事项:
我已经使用Currency1,Currency2,BenchmarkType,BenchmarkDateTime和BenchmarkID创建了一个索引,但我没有达到我想要的速度。我可以创建一个更好的索引吗?
编辑#1:有人要求下面的解释结果。如果还有其他需要,请告诉我
编辑#2:有人请求两个表的DDL(我假设这是创建语句):
(此基准表存在于数据库中)
CREATE TABLE `benchmark` (
`SequenceNumber` INT(11) NOT NULL,
`BenchmarkType` TINYINT(3) UNSIGNED NOT NULL,
`BenchmarkDateTime` DATETIME NOT NULL,
`Identifier` CHAR(6) NOT NULL,
`Currency1` CHAR(3) NULL DEFAULT NULL,
`Currency2` CHAR(3) NULL DEFAULT NULL,
`AvgBMBid` DECIMAL(18,9) NOT NULL,
`AvgBMOffer` DECIMAL(18,9) NOT NULL,
`AvgBMMid` DECIMAL(18,9) NOT NULL,
`MedianBMBid` DECIMAL(18,9) NOT NULL,
`MedianBMOffer` DECIMAL(18,9) NOT NULL,
`OpenBMBid` DECIMAL(18,9) NOT NULL,
`ClosingBMBid` DECIMAL(18,9) NOT NULL,
`ClosingBMOffer` DECIMAL(18,9) NOT NULL,
`ClosingBMMid` DECIMAL(18,9) NOT NULL,
`LowBMBid` DECIMAL(18,9) NOT NULL,
`HighBMOffer` DECIMAL(18,9) NOT NULL,
`BMRange` DECIMAL(18,9) NOT NULL,
`BenchmarkId` INT(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`BenchmarkId`),
INDEX `NextBenchmarkIndex01` (`Currency1`, `Currency2`, `BenchmarkType`),
INDEX `NextBenchmarkIndex02` (`BenchmarkDateTime`, `Currency1`, `Currency2`, `BenchmarkType`, `BenchmarkId`),
INDEX `BenchmarkOptimization` (`BenchmarkType`, `BenchmarkDateTime`, `Currency1`, `Currency2`)
)
(我正在我的例行程序中创建MyCurrencyPairs表)
CREATE TEMPORARY TABLE MyCurrencyPairs
(
Pair1 VARCHAR(50),
Pair2 VARCHAR(50)
) ENGINE=memory;
CREATE INDEX IDX_MyCurrencyPairs ON MyCurrencyPairs (Pair1, Pair2);
答案 0 :(得分:1)
BenchMarkDateTime应该是索引中的第一列。
规则是,如果仅使用复合索引的一部分,则使用的部分应该是主要部分。
其次,Group By应匹配索引。
如果您可以使用“=”代替“>”进行查询,那么您的效果会更好这是一个范围检查查询。
答案 1 :(得分:0)
主要问题是MySQL无法直接使用索引来处理聚合。这是因为与MyCurrencyPairs
的加入以及您要求MIN(BenchmarkId)
同时在BenchmarkDateTime
上具有范围条件的事实。需要消除这两个以获得更好的执行计划。
让我们先看一下所需的索引和结果查询:
ALTER TABLE benchmark
ADD KEY `IDX1` (
`Currency1`,
`Currency2`,
`BenchmarkType`,
`BenchmarkDateTime`
),
ADD KEY `IDX2` (
`Currency1`,
`Currency2`,
`BenchmarkType`,
`BenchmarkId`,
`BenchmarkDateTime`
);
SELECT
(
SELECT
BenchmarkId
FROM
benchmark FORCE KEY (IDX2)
WHERE
Currency1 = ob.Currency1 AND
Currency2 = ob.Currency2 AND
BenchmarkType = ob.BenchmarkType
AND BenchmarkDateTime > IN_BeginningTime
ORDER BY
Currency1, Currency2, BenchmarkType, BenchmarkId
LIMIT 1
) AS BenchmarkId
ob.*
FROM
(
SELECT
MIN(BenchmarkDateTime),
Currency1,
Currency2,
BenchmarkType
FROM
benchmark
WHERE
BenchmarkDateTime > IN_BeginningTime
GROUP BY
Currency1, Currency2, BenchmarkType
) AS ob
INNER JOIN
MyCurrencyPairs ON Currency1 = Pair1 AND Currency2 = Pair2;
第一个变化是GROUP BY
部分发生在它自己的子查询中。这意味着它会生成Currency1, Currency2, BenchmarkType
的所有组合,甚至是那些没有出现在MyCurrencyPairs
中的组合,但除非有很多组合,否则MySQL现在可以使用索引来执行操作让这更快。此子查询使用IDX1,无需临时表或文件存储。
第二个变化是将MIN(BenchmarkId)
部分隔离到它自己的子查询中。可以使用IDX2处理该子查询中的排序,因此这里也不需要排序。 FORCE KEY (IDX2)
提示,即使是“固定值”列Currency1
,Currency2
和BenchmarkType
出现在ORDER
- 部分也需要制作MySQL优化器做正确的事。再次,这是一个权衡。如果最终结果集很大,子查询可能会失败,但我认为没有那么多行。
解释该查询提供了以下查询计划(为了便于阅读而删除了不感兴趣的列):
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+
| id | select_type | table | type | key_len | rows | Extra |
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+
| 1 | PRIMARY | <derived3> | ALL | NULL | 1809 | |
| 1 | PRIMARY | MyCurrencyPairs | ref | 106 | 2 | Using where |
| 3 | DERIVED | benchmark | range | 17 | 1225 | Using where; Using index for group-by |
| 2 | DEPENDENT SUBQUERY | benchmark | ref | 9 | 520 | Using where; Using index |
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+
我们看到所有有趣的部分都被索引正确覆盖,我们既不需要临时表也不需要文件集。
我测试数据上的时间显示这个版本大约快20倍(1.07s对0.05s),但我的基准测试表中只有大约120万行,数据分布很可能没有,所以YMMV