从表中选择MIN和MAX都比预期慢

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)

你要求访问双方.因此,如果您同时需要最小和最大列值,则索引完全扫描不可行.

您可以在此处找到更详细的分析.

  • 提供正确答案的+1和Richard Foote关于此主题的优秀博客文章的链接. (3认同)

Vin*_*rat 6

解释计划是不同的:一个MINMAX将产生一个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只有一个方向(向前或向后)并且不能同时从两端开始,这就是为什么当你要求最小值和最大值时,你得到一种效率较低的访问方法.

正如其他答案所建议的那样,如果查询需要关键效率,您可以通过在两个不同的查询中搜索最小值和最大值来运行自己的优化.


pla*_*ben 5

尝试不要在一个查询中选择索引的两个边,而是以不同的方式访问查询,如下所示:

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)中的索引。

在此处输入图片说明