您必须查找每个 where 条件和每个 join...on 条件。两者的工作方式相同。
假设我们写
select name
from customer
where customerid=37;
DBMS 必须以某种方式找到 customerid=37 的记录。如果没有索引,唯一的方法是读取表中的每条记录,将 customerid 与 37 进行比较。即使找到一条,它也无法知道只有一条,所以它必须不断寻找其他人。
如果您在 customerid 上创建索引,DBMS 可以快速搜索索引。这不是顺序搜索,而是根据数据库,二分搜索或其他一些有效方法。究竟如何并不重要,接受它比顺序快得多。然后索引将其直接带到适当的记录或记录。此外,如果您指定索引是“唯一的”,那么数据库知道只能有一个,因此它不会浪费时间寻找第二个。 (并且 DBMS 会阻止您添加第二个。)
现在考虑这个查询:
select name
from customer
where city='Albany' and state='NY';
现在我们有两个条件。如果您只有其中一个字段的索引,则 DBMS 将使用该索引来查找记录的子集,然后按顺序搜索这些记录。例如,如果您有一个关于 state 的索引,DBMS 将快速找到 NY 的第一条记录,然后依次搜索 city='Albany',并在到达 NY 的最后一条记录时停止查找。
如果您有一个包含两个字段的索引,即“在客户(州、城市)上创建索引”,那么 DBMS 可以立即放大到正确的记录。
如果您有两个单独的索引,每个字段上都有一个,DBMS 将有各种规则来决定使用哪个索引。同样,具体如何完成这取决于您使用的特定 DBMS,但基本上它会尝试保留有关记录总数、不同值的数量和值的分布的统计信息。然后它将按顺序搜索这些记录,以寻找满足另一个条件的记录。在这种情况下,DBMS 可能会观察到城市比州多得多,因此通过使用城市索引,它可以快速放大到“奥尔巴尼”记录。然后它将依次搜索这些,检查每个与“NY”的状态。如果您有加利福尼亚州奥尔巴尼的记录,这些将被跳过。
每个连接都需要某种查找。
假设我们写
select customer.name
from transaction
join customer on transaction.customerid=customer.customerid
where transaction.transactiondate='2010-07-04' and customer.type='Q';
现在 DBMS 必须决定首先读取哪个表,从那里选择适当的记录,然后在另一个表中找到匹配的记录。
如果您有关于 transaction.transactiondate 和 customer.customerid 的索引,那么最好的计划可能是查找具有此日期的所有交易,然后为每个交易找到具有匹配 customerid 的客户,然后验证客户有正确的类型。
如果您在 customer.customerid 上没有索引,那么 DBMS 可以快速找到该交易,但是对于每个交易,它必须按顺序搜索客户表以寻找匹配的 customerid。 (这可能会很慢。)
假设您拥有的唯一索引位于 transaction.customerid 和 customer.type 上。然后 DBMS 可能会使用完全不同的计划。它可能会扫描客户表中所有类型正确的客户,然后为每个客户查找该客户的所有交易,并按顺序搜索它们以找到正确的日期。
优化最重要的关键是找出真正有帮助的索引并创建这些索引。额外的,未使用的索引是数据库的负担,因为维护它们需要工作,如果它们从未使用过,这就是浪费。
您可以使用 EXPLAIN 命令确定 DBMS 将使用哪些索引来处理任何给定的查询。我一直使用它来确定我的查询是否得到了很好的优化,或者我是否应该创建额外的索引。 (阅读有关此命令的文档以了解其输出的说明。)
警告:请记住,我说过 DBMS 会统计每个表中的记录数和不同值的数量等。如果数据发生变化,EXPLAIN 今天可能会给你一个与昨天完全不同的计划。例如,如果您有一个连接两个表的查询,其中一个非常小而另一个很大,那么它会偏向于先读取小表,然后在大表中查找匹配的记录。向表中添加记录可以改变哪个更大,从而导致 DBMS 改变其计划。因此,您应该尝试对具有真实数据的数据库执行 EXPLAINS。针对每个表中包含 5 条记录的测试数据库运行比针对实时数据库运行的价值要小得多。
嗯,还有很多话要说,但我不想在这里写书。