我有一个疑问,我进行了以下测试,并获得了以下结果
Oracle - 12.2.0.1
query rewrite -> disabled
dynamic sampling -> disabled
create table test_01 as select * from all_object;
create index ix_test_01 on test_01(object_id);
-- gathered stats
-- then ...
select * from test_01 where object_id=(select max(object_id) from test_01);
Execution Plan
----------------------------------------------------------
Plan hash value: 753718840
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 134 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID BATCHED| TEST_01 | 1 | 134 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IX_TEST_01 | 1 | | 1 (0)| 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 5 | | |
| 4 | INDEX FULL SCAN (MIN/MAX) | IX_TEST_01 | 1 | 5 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OBJECT_ID"= (SELECT MAX("OBJECT_ID") FROM "TEST_01" "TEST_01"))
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
2504 bytes sent via SQL*Net to client
608 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
select /*+ first_rows(1) */ * from test_01 order by object_id desc fetch first 1 rows only;
Execution Plan
----------------------------------------------------------
Plan hash value: 905071378
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 507 | 3 (0)| 00:00:01 |
|* 1 | VIEW | | 1 | 507 | 3 (0)| 00:00:01 |
|* 2 | WINDOW NOSORT STOPKEY | | 1 | 134 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| TEST_01 | 64489 | 8438K| 3 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN DESCENDING| IX_TEST_01 | 1 | | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("from$_subquery$_002"."rowlimit_$$_rownumber"<=1)
2 - filter(ROW_NUMBER() OVER ( ORDER BY INTERNAL_FUNCTION("TEST_01"."OBJECT_ID")
DESC )<=1)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
2504 bytes sent via SQL*Net to client
608 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select * from
2 (
3 select /*+ first_rows(1) */ * from test_01 order by object_id desc
4 )
5 where rownum<=1;
Execution Plan
----------------------------------------------------------
Plan hash value: 1996576387
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 481 | 3 (0)| 00:00:01 |
|* 1 | COUNT STOPKEY | | | | | |
| 2 | VIEW | | 1 | 481 | 3 (0)| 00:00:01 |
| 3 | TABLE ACCESS BY INDEX ROWID| TEST_01 | 64489 | 8438K| 3 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN DESCENDING| IX_TEST_01 | 1 | | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(ROWNUM<=1)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
3 consistent gets
0 physical reads
0 redo size
2504 bytes sent via SQL*Net to client
608 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
-我应从上述查询中选择哪一个,为什么?
qry-1外观最有效,因为子查询的结果是单个id。
qry-2和qry-3构建已排序值的子集(VIEW),然后从已排序集中选择第一行
查询3比查询1或查询2快14%。确定哪个查询更好的最佳方法是进行实验。
尽管我同意贾斯汀·凯夫(Justin Cave)的观点,这些性能比较可能毫无意义。通常,转换为基于集合的处理要比优化循环中的微小查询要好。
但是使用不同的方法比较效果很有用。查看执行计划并使用AUTOTRACE
是一个很好的开始,但是直接衡量查询的运行时间是衡量性能的最佳方法。
我从您的原始代码开始
create table test_01 as select * from all_objects;
create index ix_test_01 on test_01(object_id);
begin
dbms_stats.gather_table_stats(user, 'TEST_01');
end;
/
测试是PL / SQL块。 PL / SQL是完美的工具,因为它在服务器上运行,并且不受客户端或网络时间的影响。我使用IDE来测量时间,但是您也可以使用DBMS_UTILITY.GET_TIME在服务器上直接测量它,以得到更准确的结果。
大多数Oracle数据库位于共享服务器上,因此,结果不受其他工作负载的影响很重要。为了避免干扰,我通常会按顺序运行测试用例,然后重复执行,扔掉最高价和最低价,然后取平均值。
--Query 1 (2.74 seconds): 2.718, 2.758, 2.706, 2.754, 2.749.
declare
type v_test_tab is table of test_01%rowtype;
v_tests v_test_tab;
begin
for i in 1 .. 100000 loop
select * bulk collect into v_tests from test_01 where object_id=(select max(object_id) from test_01);
end loop;
end;
/
--Query 2 (2.73 seconds): 2.690, 2.741, 2.757, 2.728, 2.721
declare
type v_test_tab is table of test_01%rowtype;
v_tests v_test_tab;
begin
for i in 1 .. 100000 loop
select /*+ first_rows(1) */ * bulk collect into v_tests from test_01 order by object_id desc fetch first 1 rows only;
end loop;
end;
/
--Query 3 (2.35 seconds): 2.378, 2.322, 2.306, 2.357, 2.359
declare
type v_test_tab is table of test_01%rowtype;
v_tests v_test_tab;
begin
for i in 1 .. 100000 loop
select * bulk collect into v_tests from
(
select /*+ first_rows(1) */ * from test_01 order by object_id desc
)
where rownum<=1;
end loop;
end;
/