Just a small note to show you a situation I’ve recently encountered which shows how the CBO is intelligent. I have the following two pieces of SQL
UPDATE t1 a
SET
a.padding = 'yyyyyyyy'
WHERE
a.id1 in
(SELECT
b.id2
FROM t2 b
WHERE a.id1 = a.n1 ---> spot this
);
And the second one
UPDATE t1 a SET a.padding = 'yyyyyyyy' WHERE a.id1 in (SELECT b.id2 FROM t2 b ) AND a.id1 = a.n1; ---> spot this
I would not have written the first SQL in order to restrict the updates only to records in t1 having identical id1 and n1. I would have logically issued the second one instead.
But to my surprise the CBO recognized that the where clause in the subquery (WHERE a.id1 = a.n1 ) should be applied to the main update by replacing it with the AND clause outside the brackets. Here below are the corresponding execution plans
First query
Plan hash value: 1788758844
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | 91 | 899 (2)| 00:00:03 |
| 1 | UPDATE | T1 | | | | |
|* 2 | HASH JOIN SEMI | | 1 | 91 | 899 (2)| 00:00:03 |
|* 3 | TABLE ACCESS FULL| T1 | 1 | 78 | 447 (2)| 00:00:02 |
| 4 | TABLE ACCESS FULL| T2 | 104K| 1320K| 449 (1)| 00:00:02 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."ID1"="B"."ID2")
3 - filter("A"."ID1"="A"."N1")
Note
-----
- dynamic sampling used for this statement (level=2)
Second query
Plan hash value: 1788758844
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | 1 | 91 | 899 (2)| 00:00:03 |
| 1 | UPDATE | T1 | | | | |
|* 2 | HASH JOIN SEMI | | 1 | 91 | 899 (2)| 00:00:03 |
|* 3 | TABLE ACCESS FULL| T1 | 1 | 78 | 447 (2)| 00:00:02 |
| 4 | TABLE ACCESS FULL| T2 | 104K| 1320K| 449 (1)| 00:00:02 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("A"."ID1"="B"."ID2")
3 - filter("A"."ID1"="A"."N1")
Note
-----
- dynamic sampling used for this statement (level=2)
The same plan hash value and the same predicate part. It’s funny enough.
If you want to play with the test here is the model (borrowed from Jonathan Lewis)
create table t1
as
with generator as (
select --+ materialize
rownum id
from dual
connect by
level <= 10000)
select
rownum id1,
trunc(dbms_random.value(1,1000)) n1,
lpad(rownum,10,'0') small_vc,
rpad('x',100) padding
from
generator v1,
generator v2
where
rownum <= 100000;
create table t2
as
with generator as (
select --+ materialize
rownum id
from dual
connect by
level <= 10000)
select
rownum id2,
trunc(dbms_random.value(10001,20001)) x1,
lpad(rownum,10,'0') small_vc,
rpad('x',100) padding
from
generator v1,
generator v2
where
rownum <= 100000;