多个索引可以一起工作吗?
假设我有一个包含两个字段" foo"和" bar"的数据库表。它们都不是唯一的,但是它们每个都被索引。但是,它们每个都有一个单独的索引,而不是一起索引。
现在假设我执行一个查询,例如SELECT * FROM sometable WHERE foo ='hello'AND bar ='world';`我的表中有很多行,其中foo是'hello',而有少量行是"世界"。
因此,对于数据库服务器而言,最有效的方法是使用bar索引查找bar为" world"的所有字段,然后仅返回foo为" hello"的那些行。这是O(n)
,其中n是bar为'world'的行数。
但是,我认为该过程可能会反向发生,即使用fo索引并搜索结果。这将是" O(m)",其中m是foo为" hello"的行数。
那么,Oracle足够聪明,可以在这里有效地进行搜索吗?那其他数据库呢?还是有某种方法可以在查询中告诉它以正确的顺序进行搜索?也许通过将" bar ='world"放在" WHERE"子句的前面?
解决方案
是的,我们可以将查询的"提示"提供给Oracle。这些提示伪装为对数据库的注释(" / * HINT * /"),并且主要针对特定于供应商的。因此,对一个数据库的一个提示将不适用于另一数据库。
我将在此处使用索引提示,这是小表的第一个提示。看这里。
另一方面,如果经常搜索这两个字段,为什么不在这两个字段上创建索引?我没有正确的语法,但可能会像
CREATE INDEX IX_BAR_AND_FOO on sometable(bar,foo);
这样,数据检索应该很快。如果串联是唯一的,那么我们只需创建一个唯一的索引,该索引应该快如闪电。
So is Oracle smart enough to search efficiently here?
简单的答案是"可能"。每个数据库供应商都有很多非常聪明的人致力于优化查询优化器,因此它可能正在做我们甚至没有想到的事情。而且,如果我们更新统计信息,它可能还会做更多的事情。
我确定我们也可以让Oracle显示查询计划,以便我们可以准确地看到首先使用哪个索引。
我们可以提供有关使用哪个索引的提示。我不熟悉Oracle,但是在Mysql中,我们可以使用USE | IGNORE | FORCE_INDEX(有关更多详细信息,请参见此处)。为了获得最佳性能,我们应该使用组合索引。
最好的方法是将foo添加到bar的索引中,或者将bar添加到foo的索引中(或者两者都添加)。如果foo的索引还包含bar上的索引,那么该添加索引级别将不会影响foo索引在该索引的任何当前使用中的效用,也不会明显影响维护该索引的性能,但是它将使数据库具有更多的索引。在示例中优化查询时要使用的信息。
比这更好。
索引查找总是比全表扫描更快。因此,在幕后,Oracle(和SQL Server)将首先在两个索引上定位行范围。然后,它将查看哪个范围更短(看到它是一个内部联接),并将迭代更短的范围以找到两个范围较大的匹配项。
Oracle几乎肯定会使用最有选择性的索引来驱动查询,我们可以通过解释计划进行检查。
此外,Oracle可以通过两种方式结合使用两个索引-它可以将btree索引转换为位图并对它们执行位图ANd操作,或者可以对两个索引返回的rowid进行哈希联接。
这里一个重要的考虑因素可能是要查询的值之间的任何相关性。如果foo ='hello'占表中值的80%,而bar ='world'占表中值的10%,则Oracle将估计查询将返回表行的0.8 * 0.1 = 8%。但是,这可能是不正确的,查询实际上可能返回10%的rwos甚至0%的行,具体取决于值的相关程度。现在,根据表中这些行的分布,使用索引查找它们可能不是很有效。我们可能仍然需要访问(例如)70%或者表块以检索所需的行(google为"聚集因子"),在这种情况下,如果Oracle正确地获得了估算值,则将执行有效的表扫描。
我相信,在11g中,我们可以收集多列统计信息来帮助解决这种情况。在9i和10g中,我们可以使用动态采样来很好地估计要检索的行数。
要获得执行计划,请执行以下操作:
explain plan for SELECT * FROM sometable WHERE foo='hello' AND bar='world' / select * from table(dbms_xplan.display) /
与之对比:
explain plan for SELECT /*+ dynamic_sampling(4) */ * FROM sometable WHERE foo='hello' AND bar='world' / select * from table(dbms_xplan.display) /
首先,我假设我们正在谈论好的,普通的,标准的b * -tree索引。位图索引的答案完全不同。在Oracle中,对于各种类型的索引有很多选择,它们可能会也可能不会改变答案。
至少,如果优化程序能够确定特定条件的选择性,它将使用更具选择性的索引(即,条形图上的索引)。但是,如果数据偏斜(列栏中有N个值,但是任何特定值的选择性基本上大于或者小于数据的1 / N),则我们需要在列上具有直方图才能知道优化器或者多或者少会出现哪些值。并且,如果我们正在使用绑定变量(就像所有优秀的OLTP开发人员都应该使用的那样),则取决于Oracle版本,我们可能会遇到绑定变量偷看的问题。
潜在地,Oracle甚至可以将两个b * -tree索引动态转换为位图,并组合位图,以便使用这两个索引来查找需要检索的行。但这是一个非常不寻常的查询计划,尤其是在只有两列且其中一列具有高度选择性的情况下。
以利
我们在评论中写道:
Unfortunately, I have a table with lots of columns each with their own index. Users can query any combination of fields, so I can't efficiently create indexes on each field combination. But if I did only have two fields needing indexes, I'd completely agree with your suggestion to use two indexes. – Eli Courtwright (Sep 29 at 15:51)
这实际上是非常关键的信息。有时,程序员在提问时会比自己聪明。他们试图将问题简化为精要点,但往往过于简化而错过了获得最佳答案的机会。
正是这种情况才发明了位图索引的原因-处理在where子句中使用未知列组的时间。
以防万一有人说BMI仅适用于低基数列,可能不适用于情况。低可能不会像我们想象的那么小。唯一真正的问题是DML与表的并发性。必须是单线程的,否则必须很少使用。