15

假设我有一个包含两个字段“foo”和“bar”的数据库表。它们都不是唯一的,但它们中的每一个都被索引。但是,它们不是一起索引,而是每个都有一个单独的索引。

现在假设我执行一个查询,例如SELECT * FROM sometable WHERE foo='hello' AND bar='world'; 我的表,其中 foo 为“hello”的大量行和 bar 为“world”的少量行。

所以数据库服务器在底层做的最有效的事情是使用 bar 索引来查找 bar 为 'world' 的所有字段,然后只返回 foo 为 'hello' 的那些行。这是O(n)其中 n 是 bar 为“world”的行数。

但是,我想这个过程可能会反过来发生,即使用 fo 索引并搜索结果。这将是O(m)其中 m 是 foo 是“hello”的行数。

那么 Oracle 是否足够聪明,可以在这里高效搜索?其他数据库呢?或者有什么方法可以在我的查询中告诉它以正确的顺序搜索?也许通过将条款bar='world'放在首位WHERE

4

9 回答 9

12

Oracle 几乎肯定会使用最具选择性的索引来驱动查询,您可以使用解释计划检查这一点。

此外,Oracle 可以通过几种方式结合使用这两个索引——它可以将 btree 索引转换为位图并对它们执行位图 AND 操作,或者它可以对两个索引返回的 rowid 执行哈希连接。

这里的一个重要考虑因素可能是被查询的值之间的任何相关性。如果 foo='hello' 占表中 80% 的值,bar='world' 占 10%,那么 Oracle 将估计查询将返回 0.8*0.1= 8% 的表行。但是,这可能不正确 - 查询实际上可能返回 10% 的 rwo 甚至 0% 的行,具体取决于值的相关程度。现在,根据这些行在整个表中的分布情况,使用索引来查找它们可能效率不高。您可能仍需要访问(例如)70% 或表块来检索所需的行(谷歌为“聚类因子”),在这种情况下,如果 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)
/
于 2008-09-29T15:26:03.207 回答
4

伊莱,

你在评论中写道:

不幸的是,我有一张表,里面有很多列,每列都有自己的索引。用户可以查询任意字段组合,因此我无法高效地为每个字段组合创建索引。但是,如果我确实只有两个需要索引的字段,我完全同意您使用两个索引的建议。– Eli Courtwright(9 月 29 日 15:51)

这实际上是相当重要的信息。有时,程序员在提问时会比自己聪明。他们试图将问题提炼成最重要的问题,但往往过于简化而错过了最佳答案。

这种情况正是发明位图索引的原因——处理在 where 子句中使用未知列组的时间。

以防万一有人说 BMI 仅适用于低基数列,可能不适用于您的情况。低可能没有你想象的那么小。唯一真正的问题是 DML 与表的并发性。必须是单线程或罕见的才能工作。

于 2008-10-07T18:48:15.257 回答
3

是的,您可以通过查询向 Oracle 提供“提示”。这些提示伪装成数据库的注释(“/* HINT */”)并且主要是特定于供应商的。因此,一个数据库的一个提示将不适用于另一个数据库。

我会在这里使用索引提示,小表的第一个提示。见这里

另一方面,如果您经常搜索这两个字段,为什么不在这两个上创建索引呢?我没有正确的语法,但它会像

CREATE INDEX IX_BAR_AND_FOO on sometable(bar,foo);

这样数据检索应该非常快。如果连接是唯一的,您只需创建一个应该快如闪电的唯一索引。

于 2008-09-29T15:14:06.327 回答
3

首先,我假设你在谈论好的、正常的、标准的 b*-tree 索引。位图索引的答案完全不同。对于 Oracle 中的各种类型的索引,有很多选项可能会也可能不会改变答案。

至少,如果优化器能够确定特定条件的选择性,它将使用更具选择性的索引(即柱上的索引)。但是,如果您有偏斜的数据(列栏中有 N 个值,但任何特定值的选择性基本上大于或小于数据的 1/N),您需要在列上有一个直方图才能告诉优化器哪些值或多或少有可能。如果您使用绑定变量(所有优秀的 OLTP 开发人员都应该这样做),根据 Oracle 版本,您可能会遇到绑定变量窥视的问题。

潜在地,Oracle 甚至可以将两个 b*-tree 索引动态转换为位图,并将位图组合起来,以便使用这两个索引来查找需要检索的行。但这是一个相当不寻常的查询计划,特别是如果只有两列,其中一列是高度选择性的。

于 2008-09-29T15:29:19.673 回答
2

那么 Oracle 是否足够聪明,可以在这里高效搜索?

简单的答案是“可能”。每个数据库供应商都有很多非常聪明的人致力于优化查询优化器,所以它可能正在做你甚至没有想到的事情。如果你更新统计数据,它可能会做得更多。

于 2008-09-29T15:15:32.983 回答
1

我相信您也可以让 Oracle 显示一个查询计划,这样您就可以准确地看到首先使用哪个索引。

于 2008-09-29T15:16:23.870 回答
1

最好的方法是将 foo 添加到 bar 的索引,或将 bar 添加到 foo 的索引(或两者)。如果 foo 的索引还包含 bar 上的索引,则该额外的索引级别不会影响 foo 索引在该索引的任何当前使用中的效用,也不会明显影响维护该索引的性能,但它会给数据库额外的用于优化查询的信息,例如示例中的。

于 2008-09-29T15:20:32.440 回答
1

它比那更好。

索引搜索总是比全表扫描快。因此,在幕后 Oracle(以及 SQL 服务器)将首先在两个索引上定位行的范围。然后它将查看哪个范围更短(看到它是一个内部连接),它会迭代更短的范围以找到两者中较大的匹配项。

于 2008-09-29T15:24:42.137 回答
0

您可以提供有关使用哪个索引的提示。我对 Oracle 不熟悉,但在 Mysql 中,您可以使用 USE|IGNORE|FORCE_INDEX(有关详细信息,请参阅此处)。为了获得最佳性能,您应该使用组合索引。

于 2008-09-29T15:17:20.687 回答