我有一个名为student的表,我想获得最大和最小分数,所以我以第一种方式编写sql:
select max(score),min(score) from student;
和第二种方式:
select max(score) from student;
select min(score) from student;
我从互联网上搜索,他们说第二种方式更好,因为oracle无法同时扫描索引。但第二种方式无法确保相同的数据,因为它会进行两次搜索。如何修复它?
答案 0 :(得分:6)
将第二种方法中的两个查询合并为一个查询:
select
(select max(score) from student),
(select min(score) from student)
from dual;
该解决方案使用两个快速索引扫描。它应该比选项1或2运行得更快并且也是一致的。
为什么最简单的解决方案不起作用?
似乎Oracle 应该能够以最佳方式运行:
select max(score),min(score) from student;
之前我见过这个查询,见过人们讨论过,Oracle甚至还有特殊的访问路径来获取max和min: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
。这可能比全表扫描更好,但对于大型索引来说仍然很昂贵。
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)
操作。这可能和它一样快,尽管它只返回了答案的一半。使用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 - 将MIN和MAX与子查询相结合
将两者与子查询相结合需要更多代码,但结果将比选项1中的简单查询快得多。成本看起来略高于选项2成本的两倍,但是当你考虑额外的一轮时-trip到数据库,选项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 |
------------------------------------------------------------------------------------------
答案 1 :(得分:1)
select ma,mi from
(select max(score) ma from student) a,
(select min(score) mi from student) b