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

Which method is better when I want to search the min and max result in oracle?

我有一个叫学生的table,我想得到最高分和最低分,所以我用第一种方式写sql:

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

第二种方式:

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

我在网上搜索,他们说第二种方式更好,因为oracle不能扫描相同的索引time.But第二种方式不能确保相同的数据源,因为它做了两次searching.How 修复它?

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

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

该解决方案使用两次快速索引扫描。它应该 运行 比选项 1 或 2 更快,并且也将是一致的。


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

看起来 Oracle 应该 有办法运行 最佳地做到这一点:

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

我以前见过这个查询,看到人们讨论过它,Oracle 甚至有特殊的访问路径来获取最大值和最小值:INDEX FULL SCAN (MIN/MAX)。但它似乎无法同时执行最小值和最大值,我不确定为什么。

很难证明 Oracle 不能 做某事。也许稍后会有人进来证明我错了。我的答案基于 Richard Foote 的 this article,他可能是世界顶级的 Oracle 索引专家。我在下面包含了一些简单的测试。示例模式看起来像是 Oracle 在一次查询中自动使用 INDEX FULL SCAN (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);

选项 1:具有最小值和最大值的最简单查询 - 不起作用

最简单的查询使用 INDEX FAST FULL SCAN。这可能比完整 table 扫描更好,但对于大型索引来说仍然很昂贵。

explain plan for select max(score),min(score) from student;
select * from table(dbms_xplan.display);

Plan hash value: 4052181173

-------------------------------------------------------------------------------------
| Id  | Operation             | Name        | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT      |             |     1 |     5 |   972   (2)| 00:00:01 |
|   1 |  SORT AGGREGATE       |             |     1 |     5 |            |          |
|   2 |   INDEX FAST FULL SCAN| STUDENT_IDX |  1600K|  7812K|   972   (2)| 00:00:01 |
-------------------------------------------------------------------------------------

选项 2 - 一次查询中只有 MIN 或 MAX

运行一次一个导致最优计划,成本超低3。它有INDEX FULL SCAN (MIN/MAX)操作。这可能是它得到的最快速度,尽管它只是 returns 一半的答案。使用 MIN 而不是 MAX returns 相同的计划。

--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 - 将 MIN 和 MAX 与子查询组合

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

还有其他方法可以在一个查询中执行此操作,例如使用 UNION 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 |
------------------------------------------------------------------------------------------
select ma,mi from
(select max(score) ma from student) a,
(select min(score) mi from student) b