我有一个类似于以下的表结构:
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.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.
| 归档时间: |
|
| 查看次数: |
8029 次 |
| 最近记录: |