本教程介绍如何使用 Explain 报告查找和修复有问题的(缓慢的)查询。它使用 DBT-3 数据库,并从以下简单的查询示例开始。
SELECT * FROM orders
WHERE YEAR(o_orderdate) = 1992 AND MONTH(o_orderdate) = 4
AND o_clerk LIKE '%0223';
如下图所示,查询示例首先在可视化 SQL 编辑器中执行。接下来,通过单击 orders
表上时出现的信息。
您也可以切换到表格 Explain,如下图所示。使用下拉列表在可视化和表格表示之间切换。
关于查询的问题
为什么此查询会生成全表扫描?
为什么索引
o_orderdate
列没有作为可能的键出现?
仔细观察,还会注意到索引列正在表达式 "WHERE YEAR(o_orderdate) = 1992 AND MONTH(o_orderdate) = 4"
中使用,因此未使用索引。要使用现有索引,您可以按如下方式调整查询。
SELECT * FROM orders
WHERE o_orderdate BETWEEN '1992-04-01' AND '1992-04-30'
AND o_clerk LIKE '%0223';
更新后的查询示例会在可视化 Explain 图像中生成 索引范围扫描
,替换上一个查询示例生成的 全表扫描
。接下来的两张图显示了修改后的查询示例的可视化和表格表示。
请注意差异。“类型”从 ALL
更改为 range
,可能的键(和使用的键)从 NULL
更改为 i_o_orderdate
,扫描的行数从 150 万更改为约 3.3 万。但是,扫描 3.3 万行而只返回 18 行是不必要的,因此可以将重点转移到 o_clerk
列。下一个查询示例(和表格 Explain 图)添加了以下索引,这些索引应该可以提高性能。
CREATE INDEX i_o_clerk ON orders(o_clerk);
新索引未被视为可能的键,因为查询正在搜索 o_clerk
列的后缀,而索引不适用于后缀(尽管它们适用于前缀)。相反,这个简单的例子可以使用完整的业务员 ID。按如下方式调整查询将显示更好的结果。
SELECT * FROM orders
WHERE o_orderdate BETWEEN '1992-04-01' AND '1992-04-30'
AND o_clerk LIKE 'Clerk#000000223';
以下数字分别表示可视化 Explain 和表格 Explain 中更新后的查询示例的效果。
新 o_clerk
索引被考虑和使用,查询扫描了 1546 行而不是 32642 行,查询执行时间从 0.281 秒提高到 0.234 秒。但是,EXPLAIN
估计此查询扫描 1546 行以返回 18 行。再次查看查询后,请考虑多列索引可以满足基于 o_orderdate
和 o_clerk
列的 WHERE
子句的条件,如下一个语句所示。
CREATE INDEX io_clerk_date ON orders(o_clerk, o_orderdate)
o_clerk
在索引中显示为第一列,因为 o_orderdate
使用范围。
现在,执行调整后的查询会产生更好的结果。估计扫描并返回了 18 行,查询示例的执行时间为 0.234 秒,如下一个可视化 Explain 和表格 Explain 图所示。
下表总结了本教程中对查询所做修改的结果。
表 7.2 DBT-3 Explain 教程查询比较
类型 | 可能的键 | 键 | 扫描的行数 | 持续时间(秒) | 额外信息 | 返回的行数 |
---|---|---|---|---|---|---|
all | NULL | NULL | 1.50M | 1.201 | 使用 where | 18 |
range | i_o_orderdate | i_o_orderdate | 32642 | 0.281 | 使用索引条件;使用 where | 18 |
range | i_o_orderdate, i_o_clerk | i_o_clerk | 1546 | 0.234 | 使用索引条件;使用 where | 18 |
range | i_o_orderdate, i_o_clerk, i_o_clerk_date | i_o_clerk_date | 18 | 0.234 | 使用索引条件 | 18 |