RGO*_*RGO 26 sql oracle oracle11g oracle11gr2
我有一个MYTABLE
带有日期列的表,SDATE
它是表的主键,并且有一个唯一的索引.
当我运行此查询时:
SELECT MIN(SDATE) FROM MYTABLE
Run Code Online (Sandbox Code Playgroud)
它会立即给出答案.同样的情况发生在:
SELECT MAX(SDATE) FROM MYTABLE
Run Code Online (Sandbox Code Playgroud)
但是,如果我一起查询:
SELECT MIN(SDATE), MAX(SDATE) FROM MYTABLE
Run Code Online (Sandbox Code Playgroud)
它需要更多的时间来执行.我分析了计划并发现当查询最小值或最大值时,它使用INDEX FULL SCAN(MIN/MAX),但是当两者同时被查询时,它会进行全表扫描.
为什么?
测试数据:
版 11g
create table MYTABLE
(
SDATE DATE not null,
CELL VARCHAR2(10),
data NUMBER
)
tablespace CHIPS
pctfree 10
pctused 40
initrans 1
maxtrans 255
storage
(
initial 64K
minextents 1
maxextents unlimited
);
alter table MYTABLE
add constraint PK_SDATE primary key (SDATE)
using index
tablespace SYSTEM
pctfree 10
initrans 2
maxtrans 255
storage
(
initial 64K
minextents 1
maxextents unlimited
);
Run Code Online (Sandbox Code Playgroud)
装载表:
declare
i integer;
begin
for i in 0 .. 100000 loop
insert into MYTABLE(sdate, cell, data)
values(sysdate - i/24, 'T' || i, i);
commit;
end loop;
end;
Run Code Online (Sandbox Code Playgroud)
收集统计数据:
begin
dbms_stats.gather_table_stats(tabname => 'MYTABLE', ownname => 'SYS');
end;
Run Code Online (Sandbox Code Playgroud)
计划1:
计划2:
avi*_*avi 12
索引完全扫描只能访问索引的一侧.当你在做的时候
SELECT MIN(SDATE), MAX(SDATE) FROM MYTABLE
Run Code Online (Sandbox Code Playgroud)
你要求访问双方.因此,如果您同时需要最小和最大列值,则索引完全扫描不可行.
您可以在此处找到更详细的分析.
解释计划是不同的:一个MIN
或MAX
将产生一个INDEX FULL SCAN (MIN/MAX)
而当两个存在时你将得到一个INDEX FULL SCAN
或一个FAST FULL INDEX SCAN
.
要理解差异,我们必须寻找一个FULL INDEX SCAN
:
在完整索引扫描中,数据库按顺序读取整个索引.
换句话说,如果索引在一个VARCHAR2
字段上,Oracle将获取索引的第一个块,该索引将包含例如以字母"A"开头的所有条目,并将按字母顺序逐块读取所有条目,直到最后一个条目( "A"到"Z").Oracle可以这种方式处理,因为条目是在二叉树索引中排序的.
当您INDEX FULL SCAN (MIN/MAX)
在解释计划中看到时,这是优化的结果,该优化使用以下事实:由于条目已排序,您可以在阅读第一个之后停止,如果您只对其感兴趣MIN
.如果您只对此感兴趣MAX
,Oracle可以使用相同的访问路径,但这次从最后一个条目开始,从"Z"向后读取到"A".
截至目前,a FULL INDEX SCAN
只有一个方向(向前或向后)并且不能同时从两端开始,这就是为什么当你要求最小值和最大值时,你得到一种效率较低的访问方法.
正如其他答案所建议的那样,如果查询需要关键效率,您可以通过在两个不同的查询中搜索最小值和最大值来运行自己的优化.
尝试不要在一个查询中选择索引的两个边,而是以不同的方式访问查询,如下所示:
select max_date, min_date
from (select max(sdate) max_date from mytable),
(select min(sdate) min_date from mytable)
Run Code Online (Sandbox Code Playgroud)
将导致优化器以嵌套循环(在本例中为两次)访问INDEX_FULL_SCAN(MIN / MAX)中的索引。