我有一个类似于以下的表结构:
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上放置索引,则数据库将选择使用其中一个索引或另一个索引,而不是两者.在按第一个条件过滤后,数据库始终必须对第二个条件的结果进行全面扫描.
Oracle可以使用这两个索引.你没有正确的两个指数.
考虑一下:如果查询计划首先使用您的索引mail.sent_date
,它会从中得到mail
什么?它得到你所在条款范围内的所有mail.id
s ,是吗?mail.sent_date
where
所以它mail_to
与你在你的条款中给出的mail.id
s 列表有关.此时,Oracle决定扫描表以匹配s而不是使用索引.mail.name
where
mail_to.mail_id
mail_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_id
s要查找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
.
归档时间: |
|
查看次数: |
8029 次 |
最近记录: |