跨多个表的复合索引的等价物?

jth*_*thg 9 sql oracle

我有一个类似于以下的表结构:

create table MAIL (
  ID        int,
  FROM      varchar,
  SENT_DATE date
);

create table MAIL_TO (
  ID      int,
  MAIL_ID int,
  NAME      varchar
);
Run Code Online (Sandbox Code Playgroud)

我需要运行以下查询:

select m.ID 
from MAIL m 
  inner join MAIL_TO t on t.MAIL_ID = m.ID
where m.SENT_DATE between '07/01/2010' and '07/30/2010'
  and t.NAME = 'someone@example.com'
Run Code Online (Sandbox Code Playgroud)

有没有办法设计索引,以便两个条件都可以使用索引?如果我在MAIL.SENT_DATE上放置索引并在MAIL_TO.NAME上放置索引,则数据库将选择使用其中一个索引或另一个索引,而不是两者.在按第一个条件过滤后,数据库始终必须对第二个条件的结果进行全面扫描.

tpd*_*pdi 7

Oracle可以使用这两个索引.你没有正确的两个指数.

考虑一下:如果查询计划首先使用您的索引mail.sent_date,它会从中得到mail什么?它得到你所在条款范围内的所有mail.ids ,是吗?mail.sent_datewhere

所以它mail_to与你在你的条款中给出的mail.ids 列表有关.此时,Oracle决定扫描表以匹配s而不是使用索引.mail.namewheremail_to.mail_idmail_to.name

varchars上的索引总是有问题的,Oracle确实更喜欢全表扫描.但是,如果我们给Oracle一个包含它真正想要使用的列的索引,并且根据总表行和统计信息,我们可以让它使用它.这是索引:

 create index mail_to_pid_name on mail_to( mail_id, name ) ; 
Run Code Online (Sandbox Code Playgroud)

这适用于索引刚刚开启的name情况,因为Oracle不只是寻找名称,而是寻找a mail_id a name.

相反,如果基于成本的分析器确定首先去桌面mail_to并且使用你的索引更便宜mail_to.name,那么你会得到什么呢?一堆mail_to_.mail_ids要查找mail.它需要查找包含这些ID 某些sent_dates的行,因此:

 create index mail_id_sentdate on mail( sent_date, id ) ; 
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下,我将sent_date第一个放在索引中,id第二个.(这更直观了.)

同样,回家点是:在创建索引时,您不仅要考虑where子句中的列,还要考虑连接条件中的列.


更新

jthg:是的,它总是取决于数据的分布方式.并且在表中有多少行:如果非常多,Oracle将执行表扫描和散列连接,如果很少,它将执行表扫描.您可以颠倒两个索引中任何一个的顺序.通过将sent_date放在第二个索引中,我们完全消除了索引的大部分需求sent_date.

  • @jthg:作为大多数现代引擎,Oracle可以通过避免访问表来使用索引中的列而不是表中的列:如果它扫描索引并找到rowId,然后必须返回到磁盘要获取包含这些rowIds的表页面,这需要付出很多努力,特别是如果页面广泛分布在磁盘上.如果搜索的第二列是索引的一部分,则可以避免此额外的磁盘访问.再加上索引的较小记录大小(通常少得多的列),因此数据分布在较少的页面上,因此这种方式的I/O较少. (2认同)