在oracle中搜索最小和最大结果时,哪种方法更好?

在oracle中搜索最小和最大结果时,哪种方法更好?,oracle,oracle11g,oracle10g,Oracle,Oracle11g,Oracle10g,我有一个名为student的表,我想得到最大和最小分数,所以我用第一种方法编写sql: select max(score),min(score) from student; 第二种方式: select max(score) from student; select min(score) from student; 我从互联网上搜索,他们说第二种方法更好,因为oracle不能同时扫描索引。但是第二种方法不能确保相同的数据源,因为它进行了两次搜索。如何修复它?将第二种方法的两个查询合并为一个查询

我有一个名为student的表,我想得到最大和最小分数,所以我用第一种方法编写sql:

select max(score),min(score) from student;
第二种方式:

select max(score) from student;
select min(score) from student;

我从互联网上搜索,他们说第二种方法更好,因为oracle不能同时扫描索引。但是第二种方法不能确保相同的数据源,因为它进行了两次搜索。如何修复它?

将第二种方法的两个查询合并为一个查询:

select
    (select max(score) from student),
    (select min(score) from student)
from dual;
该解决方案使用两个快速索引扫描。它应该比选项1或2运行得更快,并且也将保持一致


为什么最简单的解决方案不起作用?

显然,Oracle应该有办法以最佳方式运行此功能:

select max(score),min(score) from student;   
我以前见过这个查询,见过人们讨论它,Oracle甚至有特殊的访问路径来获取最大值和最小值:
索引完全扫描(min/max)
。但它似乎不能同时做最小值和最大值,我不知道为什么

很难证明甲骨文做不到什么。也许以后会有人来证明我错了。我的答案基于Richard Foote,他可能是世界顶级甲骨文索引专家。下面我包括了一些简单的测试。示例模式看起来是Oracle在一个查询中自动使用
索引完整扫描(MIN/MAX)
两次的理想情况,但实际上并非如此。我的结果是使用最新版本12.2生成的

示例模式

--Create STUDENT table with 1.6 million rows, an index on score, and fresh statistics.
--drop table student;
create table student(name varchar2(100), score number not null);
insert into student select lpad('A', 20, 'A'), level from dual connect by level <= 100000;
insert into student select * from student;
insert into student select * from student;
insert into student select * from student;
insert into student select * from student;
begin
    dbms_stats.gather_table_stats(user, 'STUDENT');
end;
/
create index student_idx on student(score);
选项2-一次查询中仅显示最小值或最大值

一次运行一次可以得到成本超低3的最优计划。它具有索引完全扫描(最小/最大)操作。这可能是它得到的最快速度,尽管它只返回一半的答案。使用
MIN
而不是
MAX
返回相同的计划

--MIN works the same way
explain plan for select max(score) from student;
select * from table(dbms_xplan.display);

Plan hash value: 3501948619

------------------------------------------------------------------------------------------
| Id  | Operation                  | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |             |     1 |     5 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE            |             |     1 |     5 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| STUDENT_IDX |     1 |     5 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------------------------
选项3-将最小值和最大值与子查询相结合

将这两个查询与子查询相结合需要更多的代码,但结果将比选项1中更简单的查询快得多。成本看起来略高于选项2的两倍,但如果考虑到额外的数据库往返,选项3将是最快的

在一个查询中还有其他方法可以做到这一点,例如使用
联合ALL

explain plan for
select
    (select max(score) from student),
    (select min(score) from student)
from dual;

select * from table(dbms_xplan.display);

Plan hash value: 661746414

------------------------------------------------------------------------------------------
| Id  | Operation                  | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |             |     1 |       |     8   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE            |             |     1 |     5 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| STUDENT_IDX |     1 |     5 |     3   (0)| 00:00:01 |
|   3 |  SORT AGGREGATE            |             |     1 |     5 |            |          |
|   4 |   INDEX FULL SCAN (MIN/MAX)| STUDENT_IDX |     1 |     5 |     3   (0)| 00:00:01 |
|   5 |  FAST DUAL                 |             |     1 |       |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------

制定执行计划。这将告诉您索引是否可以正确使用。我确信您在进行这些比较之前确实刷新了缓存(共享池?),对吗?如果你的理论是正确的,那么在上一次测试中应该可以看到这一点,你将
min
max
进行比较,但事实并非如此。我们唯一能看到实际行数和字节数的地方是第一个测试,所以。。。另外,我建议在同一个查询中使用
min
max
更有效,因为这只会导致到数据库的一次往返。@g00dy对不起,我的措辞很混乱。我更改了选项3的描述。谢谢你指出往返时间。
explain plan for
select
    (select max(score) from student),
    (select min(score) from student)
from dual;

select * from table(dbms_xplan.display);

Plan hash value: 661746414

------------------------------------------------------------------------------------------
| Id  | Operation                  | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT           |             |     1 |       |     8   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE            |             |     1 |     5 |            |          |
|   2 |   INDEX FULL SCAN (MIN/MAX)| STUDENT_IDX |     1 |     5 |     3   (0)| 00:00:01 |
|   3 |  SORT AGGREGATE            |             |     1 |     5 |            |          |
|   4 |   INDEX FULL SCAN (MIN/MAX)| STUDENT_IDX |     1 |     5 |     3   (0)| 00:00:01 |
|   5 |  FAST DUAL                 |             |     1 |       |     2   (0)| 00:00:01 |
------------------------------------------------------------------------------------------
select ma,mi from
(select max(score) ma from student) a,
(select min(score) mi from student) b