inspecting buffer pool content using V$BH view appears unreliable - oracle

I executed this query on Oracle 19c where the partitioned table GRID_LTA has been assigned to KEEP POOL buffer and any segment related name has prefix GRID_LTA as well as primary key and indexes:
SQL> set timing on
SQL> set autotrace on
SQL> SELECT count(distinct t.AVG) n FROM MY_SCHEMA.GRID_LTA t;
N
--------------------------------------
308
Elapsed: 00:00:01.80
Execution Plan
----------------------------------------------------------
Plan hash value: 3595206837
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes| (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 |104K (1)| 00:00:05 | | |
| 1 | SORT AGGREGATE | | 1 | 13 | | | | |
| 2 | VIEW | VW_DAG_0 | 308 |4004|104K (1)| 00:00:05 | | |
| 3 | HASH GROUP BY | | 308 | 1232| 104K (1)| 00:00:05 | | |
| 4 | PARTITION LIST ALL | | 4979K| 18M| 104K (1)| 00:00:05 | 1 | 22 |
| 5 | TABLE ACCESS FULL| GRID_LTA | 4979K| 18M| 104K (1)| 00:00:05 | 1 | 22 |
--------------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
3 recursive calls
0 db block gets
**376769 consistent gets
0 physical reads**
0 redo size
581 bytes sent via SQL*Net to client
588 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
After ten second I submitted this query to inspect the content of the V$BH view:
select o.OWNER,o.object_name, count(distinct block#) k1, count(block#) k2
from sys.dba_objects o, SYS.V_$BH b
where b.OBJD = o.OBJECT_ID
and b.status != 'free'
and o.owner = 'MY_SCHEMA'
and instr(o.object_name,'GRID_LTA') > 0
group by o.OWNER,o.object_name;
Statistics
----------------------------------------------------------
27 recursive calls
0 db block gets
1764 consistent gets
0 physical reads
0 redo size
574 bytes sent via SQL*Net to client
577 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
**0 rows processed**
Now it appears the table has completely been read from buffer pool but inspecting buffer pool there isn't any block.
Did I something wrong or is there a possibile explanation ?
How is it possible inspecting the buffer pool in reliable way ?

just solved, using data_object_id instead object_id how I've seen abroad in some examples.

Related

Oracle unique constraint violation

I have a table with over 30 million records. When doing insert, I need to avoid the Unique constraint violation.
When I use this NOT EXIST approach, the insert takes forever. In fact, it couldn't finish after 24 hours of running. And I can't use the ignore_row_on_dupkey_index hint, because this table has more than 1 PK columns.
Another option is to insert in subsets. But I want to know if there's any other way before I do sub-setting.
insert into tlb1 a
select * from tlb2 b
where not exists (select 'x' from tlb1 c
where b.pk = c.pk)
The important decision depends on the numbe rof row inserted, i.e. the number of the rows in the table TBL2
If this number is rather low (say in hundreds to thousands) you may use safely your approach, provided there is an index on the PK column(s) - whoch should be to enforce the unique constraint.
Please check that the used execution plan is something like the one below
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 110 | 2860 | 113 (0)| 00:00:02 |
| 1 | LOAD TABLE CONVENTIONAL | TBL1 | | | | |
| 2 | NESTED LOOPS ANTI | | 110 | 2860 | 113 (0)| 00:00:02 |
| 3 | TABLE ACCESS FULL | TBL2 | 110 | 1430 | 3 (0)| 00:00:01 |
|* 4 | INDEX UNIQUE SCAN | TBL1_IXD | 1 | 13 | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("B"."PK"="C"."PK")
The NESTED LOOPS ANTI means that for each inserted row a single index lookup will be done to check if the key already exists in the target table.
This will work fine for a low number of inserted rows. For a large insert (millions rows) the optimizer will switch to a HASH JOIN RIGHT ANTI, i.e. all rows from both table will be joined to get th epossible duplicates.
This can take some time (but usually not 24 hours) and the approach with DML Error Logging which eliminates the need of the join.
INSERT INTO tbl1 (pk)
SELECT pk
FROM tbl3
LOG ERRORS INTO err$_tbl1 ('dedup tbl3') REJECT LIMIT UNLIMITED;
This approach will scale well especially when the number of the duplicates is low compared with the number of inserted rows. It is comparable to a normal insert:
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | 876K| 10M| 427 (1)| 00:00:06 |
| 1 | LOAD TABLE CONVENTIONAL | TBL1 | | | | |
| 2 | TABLE ACCESS FULL | TBL3 | 876K| 10M| 427 (1)| 00:00:06 |
---------------------------------------------------------------------------------

oracle filter on explain plan partitions

I'm making a proof of concept and I'm experimenting a strange behaviour.
I have a table partitioned by range by a date field and the cost of a query changes a lot if I set a fixed date or one created by SYSDATE.
These are the explain plans:
SQL> SELECT *
2 FROM TP_TEST_ELEMENTO_TRAZABLE ET
3 WHERE ET.FEC_RECEPCION
4 BETWEEN TRUNC(SYSDATE-2) AND TRUNC(SYSDATE-1)
5 ;
5109 filas seleccionadas.
Plan de Ejecuci¾n
----------------------------------------------------------
Plan hash value: 1151442660
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5008 | 85136 | 4504 (8)| 00:00:55 | | |
|* 1 | FILTER | | | | | | | |
| 2 | PARTITION RANGE ITERATOR| | 5008 | 85136 | 4504 (8)| 00:00:55 | KEY | KEY |
|* 3 | TABLE ACCESS FULL | TP_TEST_ELEMENTO_TRAZABLE | 5008 | 85136 | 4504 (8)| 00:00:55 | KEY | KEY |
------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(TRUNC(SYSDATE#!-2)<=TRUNC(SYSDATE#!-1))
3 - filter("ET"."FEC_RECEPCION">=TRUNC(SYSDATE#!-2) AND "ET"."FEC_RECEPCION"<=TRUNC(SYSDATE#!-1))
EstadÝsticas
----------------------------------------------------------
1 recursive calls
0 db block gets
376 consistent gets
0 physical reads
0 redo size
137221 bytes sent via SQL*Net to client
4104 bytes received via SQL*Net from client
342 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5109 rows processed
Using fixed dates:
SQL> SELECT *
2 FROM TP_TEST_ELEMENTO_TRAZABLE ET
3 WHERE ET.FEC_RECEPCION
4 BETWEEN TO_DATE('26/02/2017', 'DD/MM/YYYY') AND TO_DATE('27/02/2017', 'DD/MM/YYYY')
5 ;
5109 filas seleccionadas.
Plan de Ejecuci¾n
----------------------------------------------------------
Plan hash value: 3903280660
-----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5008 | 85136 | 11 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE ITERATOR| | 5008 | 85136 | 11 (0)| 00:00:01 | 607 | 608 |
|* 2 | TABLE ACCESS FULL | TP_TEST_ELEMENTO_TRAZABLE | 5008 | 85136 | 11 (0)| 00:00:01 | 607 | 608 |
-----------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("ET"."FEC_RECEPCION"<=TO_DATE(' 2017-02-27 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
EstadÝsticas
----------------------------------------------------------
1 recursive calls
0 db block gets
376 consistent gets
0 physical reads
0 redo size
137221 bytes sent via SQL*Net to client
4104 bytes received via SQL*Net from client
342 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5109 rows processed
What's the difference that produces a cost of 4504 and a cost of 11?
Thanks in advance :)
The difference is because when you use SYSDATE, it has the potential to need any partition. For example, if you are daily partitioned, then the partition you need to access will be different between today and tomorrow. As such, the plan is KEY:KEY, meaning that the actual partition is resolved at runtime.
With a fixed date, we know at compile time which partition it resolves to. And since it resolves to a single partition, it's more "accurately" costed.

How is the cardinality determined in a query?

I have two queries which return the same result set, but when reviewing the execution plans they have different values of cardinality.
The queries are:
select acq_cod
, prp
, df_val
, descr
from acqdefprp
where (prp like '%pswd%' or prp like '%Pswd%')
and prp not like '%kno%'
and prp not like '%encr%';
and
select acq_cod
, prp
, df_val
, descr
from acqdefprp
where regexp_instr(prp, 'pswd', 1,1,0,'i' ) > 0
and regexp_instr(prp, '(encr)|(kno)', 1,1,0,'i' ) = 0;
The first query has the following explain plan:
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 65 | 4485 | 6 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| acqdefprp | 65 | 4485 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("PRP" LIKE '%pswd%' OR "PRP" LIKE '%Pswd%')
AND "PRP" NOT LIKE '%kno%'
AND "PRP" NOT LIKE '%encr%')
And the explain plan for the second query is:
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 69 | 6 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| acqdefprp | 1 | 69 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------------
1 - filter(REGEXP_INSTR ("PRP",'(encr)|(kno)',1,1,0,'i') = 0
AND REGEXP_INSTR ("PRP",'pswd',1,1,0,'i') > 0 )
My question is why is the cardinality different between the two execution plans? For the first plan the cardinality (rows) is 65 and for the second it's 1?
My assumption is that this cardinality is the maximum number of rows that will be returned by each condition, if each condition was evaluated separately, and all of this based on table statistics. And that is why for my first query this assumed maximum is 65, since the WHERE conditions are a little more permissive.
And also that is why for the second query the cardinality is 1, since the regexp_instr is more restrictive.
If my assumptions are not correct, I'd really like to know what determines this cardinality number.
Thank you in advance for any help
In your case the expression are too complex for the optimizer to use basic statistics to estimate the cardinality. In these cases (it doesn't seem that you use histograms that might affect LIKE predicates) a fixed selectivity is used:
equality operator: 1%
inequality operator: 5%
So your
LIKE example is approximately (5 % + 5 % - (5 % * 5 %)) * 95 % * 95 % => 8.8 % of total table rows. - (5 % * 5 %) is the intersection because of OR operator.
REGEX example is 1 % * 5 % => 0.05 % of total table rows.
Oracle also supports extended statistics where you can compute statistics and histograms for specific expressions or correlated columns.
You comapare plans with direct WHERE conditions and with REGEXP_INSTR functions. Actually there is no difference which function to use, for oracle very difficult to give a real estimate without function execution.
For example we can create function -
CREATE OR REPLACE FUNCTION f_check(str IN VARCHAR2)
RETURN NUMBER IS
BEGIN
IF str LIKE 'A%' THEN
RETURN 1;
END IF;
RETURN -1;
END;
/
First select -
SELECT *
FROM tmptxt
WHERE dsc LIKE 'A%'
Plan hash value: 2928917536
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 121 | 4356 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TMPTXT | 121 | 4356 | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DSC" LIKE 'A%')
and with function -
SELECT *
FROM tmptxt
WHERE f_check(dsc) = 1
Plan hash value: 2928917536
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 36 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TMPTXT | 1 | 36 | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("F_CHECK"("DSC")=1)
This two queries give the same result, but plan estimate has some difference. It is not too important (fullscan in first way and fullscan in the second), just need to evaluate the whole plan, didn't dwell on the numbers.
My assumption is that this cardinality is the maximum number of rows that will be returned by each condition....
No, cardinality is the estimation of the CBO how many rows will be returned in the operation. (technicaly always >= 1).
The cardinality is calculated either from the object statistics stored in data dictionary or by dynaming sampling (details here).
Dynamic sampling are more costly (as they are calculated in each parse) but can return much precise results.
So one possible workaround to get better estimation is to use dynamic sampling. Here small demo with level 10 (which is extrem and demo only as the whole table is scanned in parsing step; but it is not a problem with 779 rows table and the cardinatlity is exact)
create table tst as
select ltrim(to_char(rownum,'09999')) prp from dual connect by level <= 999999;
select count(*) from tst where prp like '%999%';
280
select count(*) from tst where regexp_instr(prp, '999', 1,1,0,'i' ) > 0;
280
Alter session set optimizer_dynamic_sampling=10;
EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR
select * from tst where prp like '%999%';
SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 280 | 1400 | 467 (2)| 00:00:06 |
|* 1 | TABLE ACCESS FULL| TST | 280 | 1400 | 467 (2)| 00:00:06 |
--------------------------------------------------------------------------
1 - filter("PRP" IS NOT NULL AND "PRP" LIKE '%999%')
EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR
select * from tst where regexp_instr(prp, '999', 1,1,0,'i' ) > 0;
SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 280 | 1400 | 479 (5)| 00:00:06 |
|* 1 | TABLE ACCESS FULL| TST | 280 | 1400 | 479 (5)| 00:00:06 |
--------------------------------------------------------------------------
1 - filter( REGEXP_INSTR ("PRP",'999',1,1,0,'i')>0)

What reason to use indexing of particular rows in a table?

There is example of using a function-based indexes in the documentation Concepts Oracle 11G:
A function-based index is also useful for indexing only specific rows
in a table. For example, the cust_valid column in the sh.customers
table has either I or A as a value. To index only the A rows, you
could write a function that returns a null value for any rows other
than the A rows.
I can imagine only this use case: the reducing size of index, by eliminating some rows by condition. Is there other use cases when this possibility is useful?
Let's take a look at function-based indexes:
SQL> create table tab1 as select object_name from all_objects;
Table created.
SQL> exec dbms_stats.gather_table_stats(user, 'TAB1');
PL/SQL procedure successfully completed.
SQL> set autotrace traceonly
SQL> select count(*) from tab1 where lower(object_name) = 'all_tables';
Execution Plan
----------------------------------------------------------
Plan hash value: 1117438016
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 19 | 18 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 19 | | |
|* 2 | TABLE ACCESS FULL| TAB1 | 181 | 3439 | 18 (0)| 00:00:01 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter(LOWER("OBJECT_NAME")='all_tables')
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
63 consistent gets
...
As you know, all the objects have unique names, but oracle has to analyze all 181 rows and performs 63 consistent gets (physical or logical block reads)
Let's create a function-based index:
SQL> create index tab1_obj_name_idx on tab1(lower(object_name));
Index created.
SQL> select count(*) from tab1 where lower(object_name) = 'all_tables';
Execution Plan
----------------------------------------------------------
Plan hash value: 707634933
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 17 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 17 | | |
|* 2 | INDEX RANGE SCAN| TAB1_OBJ_NAME_IDX | 181 | 3077 | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(LOWER("OBJECT_NAME")='all_tables')
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
2 consistent gets
...
As you can see the cost cuts down (from 18 to 1) dramatically and there are only 2 consistent gets.
So function-based indexes can increase the performance of your application very well.

What is the big O of Oracles MAX function?

Is the time complexity of the Oracle MAX function O(1), O(log n) or O(n) with respect to the number of rows in a table?
If you have a B-tree index on the column then finding the maximum value is O(log(n)) because the answer will be the last (or first) row of the index. Values are stored in the deepest nodes of the B-tree which has a height O(log(n)).
Without an index it is O(n) because all rows must be read to determine the maximum value.
Note: The O(n) notation ignores constants but in the real world these constants cannot be ignored. The difference between reading from disk and reading from memory is several orders of magnitude. Accessing the first value of an index is likely to be performed mostly in RAM, whereas a full table scan of a huge table will need to read mostly from disk.
Realistically, it's hard to say without specifying a query, a table definition, and a query plan.
If you have a table that has no index on the column you're computing the MAX on, Oracle will have to do a full table scan. That is going to be O(n) since you've got to scan every block in the table. You can see that by looking at the query plan.
We'll generate a table with 100,000 rows and ensure that the rows are reasonably large using a CHAR(1000) column
SQL> create table foo( col1 number, col2 char(1000) );
Table created.
SQL> insert into foo
2 select level, lpad('a',1000)
3 from dual
4 connect by level <= 100000;
100000 rows created.
Now, we can look at the plan for the basic MAX operation. This is doing a full table scan (an O(n) operation)
SQL> set autotrace on;
SQL> select max(col1)
2 from foo;
MAX(COL1)
----------
100000
Execution Plan
----------------------------------------------------------
Plan hash value: 1342139204
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 4127 (1)| 00:00:50 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | TABLE ACCESS FULL| FOO | 106K| 1350K| 4127 (1)| 00:00:50 |
---------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
29 recursive calls
1 db block gets
14686 consistent gets
0 physical reads
176 redo size
527 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
If you create an index on the column you're computing the MAX of, Oracle can do a MIN/MAX scan on the index. That is an O(log n) operation if that's the plan the optimizer chooses. Of course, as a practical matter, this is functionally an O(1) operation because the height of an index is never realistically going to exceed 4 or 5-- the constant terms here are going to dominate.
SQL> create index idx_foo_col1
2 on foo( col1 );
Index created.
SQL> select max(col1)
2 from foo;
MAX(COL1)
----------
100000
Execution Plan
----------------------------------------------------------
Plan hash value: 817909383
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | INDEX FULL SCAN (MIN/MAX)| IDX_FOO_COL1 | 1 | 13 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
5 recursive calls
0 db block gets
83 consistent gets
1 physical reads
0 redo size
527 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
But then things get harder. Both MIN and MAX have the same O(log n) behavior individually. But if you have both MIN and MAX in the same query, suddenly you're back to an O(n) operation. Oracle (as of 11.2) hasn't implemented an option grab both the first block and the last block of an index
SQL> ed
Wrote file afiedt.buf
1 select min(col1), max(col1)
2* from foo
SQL> /
MIN(COL1) MAX(COL1)
---------- ----------
1 100000
Execution Plan
----------------------------------------------------------
Plan hash value: 1342139204
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 4127 (1)| 00:00:50 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | TABLE ACCESS FULL| FOO | 106K| 1350K| 4127 (1)| 00:00:50 |
---------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
4 recursive calls
0 db block gets
14542 consistent gets
0 physical reads
0 redo size
601 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
Of course, in subsequent versions of Oracle, this optimization might be implemented and this would go back to being an O(log n) operation. Of course, you can also rewrite the query to get a different query plan that goes back to being O(log n)
SQL> ed
Wrote file afiedt.buf
1 select (select min(col1) from foo) min,
2 (select max(col1) from foo) max
3* from dual
SQL>
SQL> /
MIN MAX
---------- ----------
1 100000
Execution Plan
----------------------------------------------------------
Plan hash value: 3561244922
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | INDEX FULL SCAN (MIN/MAX)| IDX_FOO_COL1 | 1 | 13 | 2 (0)| 00:00:01 |
| 3 | SORT AGGREGATE | | 1 | 13 | | |
| 4 | INDEX FULL SCAN (MIN/MAX)| IDX_FOO_COL1 | 1 | 13 | 2 (0)| 00:00:01 |
| 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Note
-----
- dynamic sampling used for this statement (level=2)
Statistics
----------------------------------------------------------
7 recursive calls
0 db block gets
166 consistent gets
0 physical reads
0 redo size
589 bytes sent via SQL*Net to client
523 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed

Resources