TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
Database & Technology 2 _ Richard Foote _ 10 things you probably dont know about Oracle's indexes.pdf
1. 10
Things
You
Might
Not
Know
About
Oracle
Indexes
Richard
Foote
The most comprehensive Oracle applications & technology content under one roof
2. Richard
Foote
• Working
in
IT
for
25
years
(scary
stuff)
• Working
with
Oracle
for
15
years
(almost
as
scary)
• Previously
employed
by
Oracle
CorporaFon
for
5
½
years
(scary
as
hell)
• Currently
employed
by
the
Australian
Federal
Government
as
a
Senior
DBA
• Responsible
for
many
large
scale,
mission
criFcal,
“life-‐dependant”
classified
Oracle
systems
• Based
in
sunny
Canberra,
Australia
• Oracle
OakTable
member
since
2002
and
Oracle
ACE
Director
since
2008
• Interests
includes
all
sports
and
music
(cool
stuff
like
David
Bowie,
Pink
Floyd,
Radiohead
and
Muse)
• richard.foote@bigpond.com
• Richard
Foote’s
Oracle
Blog:
hBp://richardfoote.wordpress.com/
The most comprehensive Oracle applications & technology content under one roof
3. 1.
Delete
More
Rows
To
Reduce
Deleted
Space
Within
An
Index
• Very
common
advice
to
rebuild
index
if
percentage
of
deleted
space
>
20%
• For
example
-‐
Metalink
Note:
122008.1
recommended
this
for
many
years
• Validate
index
structure
(Ignoring
that
it’s
expensive
and
locks
the
table)
• Claimed
rebuild
will
remove
index
“deadwood”
and
make
index
more
efficient
• However,
if
only
we
just
deleted
even
more
rows
...
The most comprehensive Oracle applications & technology content under one roof
4. 1:
If
deleted
space
>20%,
delete
some
more!!
SQL>
create
table
radiohead
(id
number,
name
varchar2(20));
Table
created.
SQL>
insert
into
radiohead
select
rownum,
'OK
COMPUTER'
from
dual
connect
by
level
<=
100000;
100000
rows
created.
SQL>
commit;
Commit
complete.
SQL>
create
index
radiohead_i
on
radiohead(id)
pckree
0;
Index
created.
SQL>
analyze
index
radiohead_i
validate
structure;
Index
analyzed.
SQL>
select
lf_rows,
del_lf_rows,
round((del_lf_rows/lf_rows)*100)
PCT_DEL,
pct_used
from
index_stats;
LF_ROWS DEL_LF_ROWS PCT_DEL PCT_USED
---------- ----------- ---------- ----------
100000 0 0 100
The most comprehensive Oracle applications & technology content under one roof
5. 1:
If
deleted
space
>20%,
delete
some
more!!
SQL>
delete
radiohead
where
mod(id,10)
IN
(2,4,6);
30000
rows
deleted.
SQL>
commit;
Commit
complete.
SQL>
analyze
index
radiohead_i
validate
structure;
Index
analyzed.
SQL>
select
lf_rows,
del_lf_rows,
(del_lf_rows/lf_rows)*100
PCT_DEL,
pct_used
from
index_stats;
LF_ROWS DEL_LF_ROWS PCT_DEL PCT_USED
---------- ----------- ---------- ----------
100000 30000 30 100
So
based
on
common
rebuild
criteria,
this
index
really
should
be
rebuilt
...
The most comprehensive Oracle applications & technology content under one roof
6. 1:
If
deleted
space
>20%,
delete
some
more!!
Well,
let’s
just
delete
some
more
rows
...
SQL>
delete
radiohead
where
mod(id,10)
=
8;
10000
rows
deleted.
SQL>
commit;
Commit
complete.
SQL>
analyze
index
radiohead_i
validate
structure;
Index
analyzed.
SQL>
select
lf_rows,
del_lf_rows,
round((del_lf_rows/lf_rows)*100)
PCT_DEL,
pct_used
from
index_stats;
LF_ROWS DEL_LF_ROWS PCT_DEL PCT_USED
---------- ----------- ---------- ----------
70000 10000 14 70
The most comprehensive Oracle applications & technology content under one roof
7. 1:
If
deleted
space
>20%,
delete
some
more!!
• What
many
don’t
realise
is
that
in
most
scenarios,
deleted
space
is
the
same
as
free
space
• It
will
generally
eventually
get
reused
• To
clean
out
deleted
space,
it
just
takes
the
leaf
block
to
be
modified
by
a
subsequent
transacFon
• The
criteria
actually
rebuilds
indexes
that
don’t
need
rebuilding,
and
• The
criteria
misses
out
on
those
indexes
that
might
actually
need
rebuilding
The most comprehensive Oracle applications & technology content under one roof
8. 2.Bitmap
Index
High
Cardinality
Columns
• Very
common
advice
to
only
use
Bitmap
Index
with
“Low”
Cardinality
Columns
• Generally
columns
with
less
than
20
disFnct
values
• Examples:
Eye/Hair
Colour,
Sex,
States
of
Australia...
• A
column
with
say
10,000
disFnct
values
would
be
far
too
many
and
result
in
huge,
inefficient
index
• A
column
on
say
person
first
name
or
surname
would
be
totally
unsuitable
for
a
Bitmap
Index
• But
would
it
really
...
The most comprehensive Oracle applications & technology content under one roof
9. 2.Bitmap
Index
High
Cardinality
Columns
SQL>
CREATE
TABLE
big_dwh_table
(id
NUMBER,
album_id
NUMBER,
arFst_id
NUMBER,
country_id
NUMBER,
format_id
NUMBER,
release_date
DATE,
total_sales
NUMBER);
Table
created.
SQL>
CREATE
SEQUENCE
dwh_seq;
Sequence
created.
SQL>
create
or
replace
procedure
pop_big_dwh_table
as
2
v_id
number;
3
v_arFst_id
number;
4
begin
5
for
v_album_id
in
1..10000
loop
6
v_arFst_id:=
ceil(dbms_random.value(0,100));
7
for
v_country_id
in
1..100
loop
8
select
dwh_seq.nextval
into
v_id
from
dual;
9
insert
into
big_dwh_table
values
(v_id,
v_album_id,
v_arFst_id,
vcountry_id,
ceil(dbms_random.value(0,4)),
trunc(sysdate-‐mod(v_id,ceil(dbms_random.value(0,1000)))),
ceil(dbms_random.value(0,500000)));
10
end
loop;
11
end
loop;
12
commit;
13
end;
14
/
Procedure
created.
SQL>
exec
pop_big_dwh_table
PL/SQL
procedure
successfully
completed.
The most comprehensive Oracle applications & technology content under one roof
10. 2.Bitmap
Index
High
Cardinality
Columns
OK,
let’s
start
with
a
B-‐Tree
Index
SQL>
CREATE
INDEX
big_dwh_album_id_i
ON
big_dwh_table(album_id)
COMPUTE
STATISTICS;
Index
created.
SQL>
SELECT
i.index_type,
i.disFnct_keys,
t.num_rows
"T
ROWS",
t.blocks
"T
BLOCKS",
i.num_rows
"I
ROWS",
i.clustering_factor
CF,
i.leaf_blocks
FROM
user_indexes
i,
user_tables
t
WHERE
i.table_name=t.table_name
and
i.index_name
=
'BIG_DWH_ALBUM_ID_I';
INDEX_TYPE DISTINCT_KEYS T ROWS T BLOCKS I ROWS CF LEAF_BLOCKS
---------- ------------- ---------- ---------- ---------- ---------- -----------
NORMAL 10000 1000000 4948 1000000 4951 2090
Note
the
album_id
column
has
a
relaFvely
“high”
cardinality
at
10,000
disFnct
values.
Also
note
the
B-‐Tree
Index
is
extremely
well
clustered
(CF
matches
blocks
in
table)
and
has
2090
leaf
blocks
...
The most comprehensive Oracle applications & technology content under one roof
11. 2.
Bitmap
Index
High
Cardinality
Columns
SQL>
SELECT
*
FROM
big_dwh_table
WHERE
album_id
=
42;
100 rows selected.
Execution Plan
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 3000 | 103 (0)|00:00:02|
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE_2 | 100 | 3000 | 103 (0)|00:00:02|
|* 2 | INDEX RANGE SCAN | BIG_DWH_2_ALBUM_ID_I | 100 | | 3 (0)|00:00:01|
-------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
19 consistent gets
100 rows processed
Note:
As
expected,
the
index
is
used
to
retrieve
the
relaFvely
few
100
rows
(0.01%
of
data).
Just
19
consistent
reads
required
...
The most comprehensive Oracle applications & technology content under one roof
12. 2.Bitmap
Index
High
Cardinality
Columns
Let’s
create
same
index
as
a
Bitmap
Index.
However,
with
10,000
disFnct
values,
unlikely
to
be
efficient,
right
?
SQL>
DROP
INDEX
big_dwh_album_id_i;
Index
dropped.
SQL>
CREATE
BITMAP
INDEX
big_dwh_album_id_i
ON
big_dwh_table(album_id);
Index
created.
SQL>
SELECT
index_name,
index_type,
disFnct_keys,
num_rows,
leaf_blocks,
blocks
FROM
user_indexes
i,
user_segments
s
WHERE
i.index_name
=
s.segment_name
and
i.index_name
=
'BIG_DWH_ALBUM_ID_I';
INDEX_NAME INDEX_TYPE DISTINCT_KEYS NUM_ROWS LEAF_BLOCKS BLOCKS
------------------ ---------- ------------- -------- ----------- ------
BIG_DWH_ALBUM_ID_I BITMAP 10000 10000 56 128
Wrong.
The
number
of
index
rows
is
just
10000,
one
for
each
disFnct
value
and
leaf
blocks
has
dropped
from
2090
to
just
56
!!
The most comprehensive Oracle applications & technology content under one roof
13. 2.
Bitmap
Index
High
Cardinality
Columns
SQL>
SELECT
*
FROM
big_dwh_table
WHERE
album_id
=
42;
100 rows selected.
Execution Plan
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 3000 | 22 (0)|00:00:01|
| 1 | TABLE ACCESS BY INDEX ROWID | BIG_DWH_TABLE | 100 | 3000 | 22 (0)|00:00:01|
| 2 | BITMAP CONVERSION TO ROWIDS| | | | | |
|* 3 | BITMAP INDEX SINGLE VALUE | BIG_DWH_ALBUM_ID_I | | | | |
-------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
11 consistent gets
100 rows processed
Note:
Not
only
is
the
index
used,
but
the
number
of
consistent
reads
has
dropped
from
19
to
just
11
!!
The most comprehensive Oracle applications & technology content under one roof
14. 2.
Bitmap
Index
High
Cardinality
Columns
The
Clustering
Factor
of
an
index
has
a
big
impact
on
the
overall
size
of
a
Bitmap
Index
This
is
due
to
having
more
conFnuous
zeros
in
the
bitmap
string
that
can
be
more
efficiently
compressed
SQL>
SELECT
i.index_name,
i.index_type,
t.num_rows
"TABLE
ROWS",
t.blocks,
i.num_rows
"INDEX
ROWS",
i.clustering_factor
CF,
i.leaf_blocks
FROM
user_indexes
i,
user_tables
t
WHERE
i.table_name=t.table_name
and
i.index_name
=
'BIG_DWH_ALBUM_ID_I';
INDEX_NAME INDEX_TYPE TABLE ROWS BLOCKS INDEX ROWS CF LEAF_BLOCKS
------------------ ---------- ---------- ------- ---------- ------ -----------
BIG_DWH_ALBUM_ID_I BITMAP 1000000 4948 10000 10000 56
Note
the
CF
as
recorded
for
Bitmap
Indexes
is
a
liyle
meaningless
as
a
specific
index
entry
covers
many
Rowids
but
the
CF
calculaFon
is
sFll
based
on
the
block
within
the
rowid
changing
from
one
index
entry
to
the
next.
Therefore,
CF
of
an
index
is
commonly
recorded
as
being
the
number
of
index
row
entries
The most comprehensive Oracle applications & technology content under one roof
15. 2.
Bitmap
Index
High
Cardinality
Columns
Create
a
copy
of
the
table
but
this
Fme
clustered
by
the
TOTAL_SALES
column
Then
create
a
Bitmap
Index
on
the
now
poorly
clustered
ALBUM_ID
column
SQL>
CREATE
TABLE
big_dwh_table_2
AS
SELECT
*
FROM
big_dwh_table
ORDER
BY
total_sales;
Table
created.
SQL>
CREATE
BITMAP
INDEX
big_dwh_2_album_id_i
ON
big_dwh_table_2(album_id);
Index
created.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>null,
tabname=>
'BIG_DWH_TABLE_2',
esFmate_percent=>
null,
cascade=>
true,
method_opt=>
'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
The most comprehensive Oracle applications & technology content under one roof
16. 2.
Bitmap
Index
High
Cardinality
Columns
SQL>
SELECT
i.index_name,
i.index_type,
t.num_rows
"TABLE
ROWS",
t.blocks,
i.num_rows
"INDEX
ROWS",
i.clustering_factor
CF,
i.leaf_blocks
FROM
user_indexes
i,
user_tables
t
WHERE
i.table_name=t.table_name
and
i.index_name
=
'BIG_DWH_2_ALBUM_ID_I';
INDEX_NAME INDEX_TYPE TABLE ROWS BLOCKS INDEX ROWS CF LEAF_BLOCKS
-------------------- ---------- ---------- ------- ---------- ------ -----------
BIG_DWH_2_ALBUM_ID_I BITMAP 1000000 4973 10000 10000 455
Note:
Leaf
Blocks
has
jumped
up
significantly
from
56
blocks
to
455
blocks
The most comprehensive Oracle applications & technology content under one roof
17. 2.
Bitmap
Index
High
Cardinality
Columns
SQL>
SELECT
*
FROM
big_dwh_table_2
WHERE
album_id
=
42;
100 rows selected.
Execution Plan
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost (%CPU)|Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100| 3000 | 22 (0)|00:00:01|
| 1 | TABLE ACCESS BY INDEX ROWID | BIG_DWH_TABLE_2 | 100| 3000 | 22 (0)|00:00:01|
| 2 | BITMAP CONVERSION TO ROWIDS| | | | | |
|* 3 | BITMAP INDEX SINGLE VALUE | BIG_DWH_2_ALBUM_ID_I | | | | |
-------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
101 consistent gets
100 rows processed
Note:
The
number
of
consistent
reads
has
now
jumped
up
from
11
to
101
...
The most comprehensive Oracle applications & technology content under one roof
18. 2.
Bitmap
Index
High
Cardinality
Columns
Of
course,
this
will
also
impact
Clustering
Factor
of
a
B-‐Tree
Index
...
SQL>
DROP
INDEX
big_dwh_2_album_id_i;
Index
dropped.
SQL>
CREATE
INDEX
big_dwh_2_album_id_i
ON
big_dwh_table_2(album_id);
Index
created.
SQL>
SELECT
i.index_name,
i.index_type,
t.num_rows
"TABLE
ROWS",
t.blocks,
i.num_rows
"INDEX
ROWS",
i.clustering_factor
CF,
i.leaf_blocks
FROM
user_indexes
i,
user_tables
t
WHERE
i.table_name=t.table_name
and
i.index_name
=
'BIG_DWH_2_ALBUM_ID_I';
INDEX_NAME INDEX_TYPE TABLE ROWS BLOCKS INDEX ROWS CF LEAF_BLOCKS
-------------------- ---------- ---------- ------- ---------- ------- -----------
BIG_DWH_2_ALBUM_ID_I NORMAL 1000000 4973 1000000 990003 2090
Although
the
index
has
same
no.
of
leaf
blocks,
the
Clustering
Factor
has
jumped
to
a
massive
990003
...
The most comprehensive Oracle applications & technology content under one roof
19. 2.
Bitmap
Index
High
Cardinality
Columns
SQL>
SELECT
*
FROM
big_dwh_table_2
WHERE
album_id
=
42;
100 rows selected.
Execution Plan
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 3000 | 103 (0)|00:00:02|
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE_2 | 100 | 3000 | 103 (0)|00:00:02|
|* 2 | INDEX RANGE SCAN | BIG_DWH_2_ALBUM_ID_I | 100 | | 3 (0)|00:00:01|
-------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
109 consistent gets
100 rows processed
Consistent
reads
has
jumped
from
19
to
109
which
is
sFll
worse
than
the
101
consistent
reads
of
the
Bitmap
Index
...
The most comprehensive Oracle applications & technology content under one roof
20. 2.
Bitmap
Index
High
Cardinality
Columns
Table
containing
person
names,
using
freshly
rebuilt
B-‐Tree
Indexes
...
NAME INDEX_TYPE NUM_ROWS DISTINCT_KEYS LEAF_BLOCKS
---------- ------------ ---------- ------------- -----------
FIRST NAME NORMAL 2378672 147446 5842
LAST NAME NORMAL 2405599 361177 6202
SQL>
select
*
from
persons
where
given_name1
=
'DAVID'
or
last_name
=
'BOWIE';
39240
rows
selected.
Execution Plan
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 174 | 36888 | 164 (0)| 00:00:01 |
| 1 | CONCATENATION | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| PERSONS | 47 | 9964 | 44 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | PERS_LAST_NAME_I | 47 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| PERSONS | 127 | 26924 | 120 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | PERS_FIRST_NAME1_I | 127 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
28600 consistent gets
The most comprehensive Oracle applications & technology content under one roof
21. 2.
Bitmap
Index
High
Cardinality
Columns
Bitmap
Indexes
are
actually
substanFally
smaller
and
more
efficient
...
NAME INDEX_TYPE NUM_ROWS DISTINCT_KEYS LEAF_BLOCKS
---------- ------------ ---------- ------------- -----------
FIRST NAME BITMAP 148208 147463 1608 (-4234)
LAST NAME BITMAP 361421 361183 2543 (-3659)
SQL>
select
*
from
persons
where
given_name1
=
'DAVID'
or
last_name
=
'BOWIE';
39240
rows
selected.
Execution Plan
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 176 | 37312 | 43 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID | PERSONS | 176 | 37312 | 43 (0)| 00:00:01 |
| 2 | BITMAP CONVERSION TO ROWIDS| | | | | |
| 3 | BITMAP OR | | | | | |
|* 4 | BITMAP INDEX SINGLE VALUE| PERS_FIRST_NAME1_I | | | | |
|* 5 | BITMAP INDEX SINGLE VALUE| PERS_LAST_NAME_I | | | | |
---------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
28483 consistent gets
The most comprehensive Oracle applications & technology content under one roof
22. 2.
Bitmap
Index
High
Cardinality
Columns
• If
you
only
use
Bitmap
Indexes
for
low
cardinality
columns,
you’re
not
taking
full
advantage
of
them
• Suitable
for
all
columns
that
are
not
approaching
uniqueness
• BUT,
only
for
Data
Warehouse
environments
due
to
locking
implicaFons
of
concurrent
transacFons
within
same
table
The most comprehensive Oracle applications & technology content under one roof
23. 3.
B-‐Tree
Index
Low
Cardinality
Columns
• Generally
B-‐Tree
Indexes
more
suitable
for
columns
with
many
disFnct
values
• Increases
selecFvity
and
makes
indexes
a
more
efficient
opFon
to
the
CBO
• However,
there
are
always
excepFons
• B-‐Tree
Index
might
be
the
perfect
candidate
for
a
column
with
as
low
as
1
disFnct
value
...
The most comprehensive Oracle applications & technology content under one roof
24. 3.
B-‐Tree
Index
Low
Cardinality
Columns
SQL>
create
table
bowie
(id
number,
code
number,
name
varchar2(50));
Table
created.
SQL>
create
index
bowie_code_i
on
bowie(code);
Index
created.
SQL>
insert
into
bowie
select
rownum,
null,
'Ziggy
Stardust
and
the
Spiders
From
Mars'
from
dual
connect
by
level
<=
1000000;
1000000
rows
created.
SQL>
update
bowie
set
code
=
42
where
mod(id,10000)
=
0;
100
rows
updated.
SQL>
commit;
Commit
complete.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>null,
tabname=>'BOWIE',
cascade=>
true,
esFmate_percent=>null,
method_opt=>'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
CODE
column
only
has
1
disFnct
value
although
most
rows
are
NULL
...
The most comprehensive Oracle applications & technology content under one roof
25. 3.
B-‐Tree
Index
Low
Cardinality
Columns
SQL>
select
blevel,
leaf_blocks,
disFnct_keys
from
dba_indexes
where
index_name='BOWIE_CODE_I';
BLEVEL LEAF_BLOCKS DISTINCT_KEYS
---------- ----------- -------------
0 1 1
SQL>
select
column_name,
num_disFnct,
num_nulls
from
dba_tab_columns
where
table_name
=
'BOWIE'
and
column_name
=
'CODE';
COLUMN_NAME NUM_DISTINCT NUM_NULLS
------------ ------------ ----------
CODE 1 999900
As
NULLs
are
not
stored
in
B-‐Tree
Indexes
by
default,
index
is
Fny
CBO
knows
there’s
only
1
disFnct
value
but
it
also
knows
most
rows
are
only
NULLs
The most comprehensive Oracle applications & technology content under one roof
26. 3.
B-‐Tree
Index
Low
Cardinality
Columns
SQL>
select
*
from
bowie
where
code
=
42;
100
rows
selected.
Execution Plan
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 4700 | 101 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| BOWIE | 100 | 4700 | 101 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | BOWIE_CODE_I | 100 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
CBO
will
happily
use
index
on
CODE
column
with
1
disFnct
value
No
histograms
necessary
as
exisFng
staFsFcs
sufficient
for
CBO
to
know
index
is
the
most
efficient
access
path
...
The most comprehensive Oracle applications & technology content under one roof
27. 4.
Index
Just
Which
Column
Values
You
Want
• By
default,
all
values
within
a
column
are
indexed
• There
are
many
scenarios
when
you
may
not
want
to
index
all
values
within
a
column
• Some
column
values
maybe
too
numerous
to
be
of
pracFcal
use
within
the
index
• Some
column
values
may
unnecessarily
inflate
size
and
efficiency
of
index
• So,
just
don't
index
them
...
The most comprehensive Oracle applications & technology content under one roof
28. 4.
Just
index
some
column
values
SQL>
CREATE
TABLE
index_some_stuff
(id
number,
status
varchar2(20),
descripFon
varchar2(50));
Table
created.
SQL>
INSERT
INTO
index_some_stuff
SELECT
rownum,
'PROCESSED',
'NOT
REALLY
INTERESTED
WITH
THIS
ROW'
FROM
DUAL
CONNECT
BY
LEVEL
<=
1000000;
1000000
rows
created.
SQL>
UPDATE
index_some_stuff
SET
status
=
'BOWIE',
descripFon
=
'ROW
OF
INTEREST'
where
mod(id,10000)=42;
100
rows
updated.
SQL>
commit;
Commit
complete.
SQL>
SELECT
status,
count(*)
FROM
index_some_stuff
GROUP
BY
status;
STATUS COUNT(*)
-------------------- ----------
PROCESSED 999900
BOWIE 100
The most comprehensive Oracle applications & technology content under one roof
29. 4.
Just
index
some
column
values
SQL>
CREATE
INDEX
index_some_stuff_i_1
ON
index_some_stuff(status);
Index
created.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>'BOWIE',
tabname=>
’INDEX_SOME_STUFF',
method_opt=>
'FOR
COLUMNS
STATUS
SIZE
5');
PL/SQL
procedure
successfully
completed.
SQL>
SELECT
*
FROM
index_some_stuff
WHERE
status
=
'BOWIE';
100
rows
selected.
Execution Plan
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 5200 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| INDEX_SOME_STUFF | 100 | 5200 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | INDEX_SOME_STUFF_I_1 | 100 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
104 consistent gets
100 rows processed
The most comprehensive Oracle applications & technology content under one roof
30. 4.
Just
index
some
column
values
But
why
bother
indexing
all
the
common
values
when
they’ll
never
be
referenced
?
SQL>
CREATE
INDEX
index_some_stuff_i_2
ON
index_some_stuff(DECODE(status,
'BOWIE',
'BOWIE',
NULL))
COMPUTE
STATISTICS;
Index
created.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>'BOWIE',
tabname=>
‘INDEX_SOME_STUFF',
method_opt=>'FOR
ALL
HIDDEN
COLUMNS
SIZE
1',
cascade=>
true);
PL/SQL
procedure
successfully
completed.
SQL>
SELECT
index_name,
blevel,
leaf_blocks,
disFnct_keys,
num_rows
FROM
dba_indexes
WHERE
owner='BOWIE'
AND
table_name='INDEX_SOME_STUFF';
INDEX_NAME BLEVEL LEAF_BLOCKS DISTINCT_KEYS NUM_ROWS
------------------------------ ---------- ----------- ------------- ----------
INDEX_SOME_STUFF_I_2 0 1 1 100
INDEX_SOME_STUFF_I_1 2 2924 2 1000000
The
index
is
only
a
fracFon
the
size
of
the
previous
index
as
it
only
contains
the
one
index
entry
of
interest
The most comprehensive Oracle applications & technology content under one roof
31. 4.
Just
index
some
column
values
SQL>
SELECT
*
FROM
bowie.index_some_stuff
WHERE(DECODE(status,
'BOWIE',
'BOWIE',
null))
=
'BOWIE';
100 row selected.
----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 100 | 5300 | 101 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| INDEX_SOME_STUFF | 100 | 5300 | 101 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | INDEX_SOME_STUFF_I_2 | 100 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
102 consistent gets
100 rows processed
Note:
consistent
gets
have
now
reduced
down
from
104
to
102
The most comprehensive Oracle applications & technology content under one roof
32. 4.
Just
index
some
column
values
Can
use
zero
sized
unusable
indexes
to
your
advantage
to
index
only
useful
porFons
of
a
table.
Most
data
here
is
processed:
SQL>
create
table
bowie_stuff
(id
number,
processed
varchar2(10));
Table
created.
SQL>
insert
into
bowie_stuff
select
rownum,
'YES'
from
dual
connect
by
level
<=
1000000;
1000000
rows
created.
SQL>
commit;
Commit
complete.
SQL>
update
bowie_stuff
set
processed
=
‘NO’
where
id
in
(999990,
999992,
999994,
999996,
999998);
5
rows
updated.
SQL>
commit;
Commit
complete.
SQL>
create
index
bowie_stuff_i
on
bowie_stuff(processed)
pckree
0;
Index
created.
The most comprehensive Oracle applications & technology content under one roof
33. 4.
Just
index
some
column
values
SQL>
select
index_name,
leaf_blocks
from
dba_indexes
where
index_name
=
'BOWIE_STUFF_I';
INDEX_NAME LEAF_BLOCKS
------------------------------ -----------
BOWIE_STUFF_I 1877
SQL>
select
segment_name,
blocks
from
dba_segments
where
segment_name
=
'BOWIE_STUFF_I';
SEGMENT_NAME BLOCKS
-------------------- ----------
BOWIE_STUFF_I 1920
SQL>
exec
dbms_stats.gather_table_stats(ownname=>'BOWIE',
tabname=>'BOWIE_STUFF',
esFmate_percent=>null,
cascade=>
true,
method_opt=>
'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>'BOWIE',
tabname=>'BOWIE_STUFF',
esFmate_percent=>null,
method_opt=>
'FOR
COLUMNS
PROCESSED
SIZE
5');
PL/SQL
procedure
successfully
completed.
The most comprehensive Oracle applications & technology content under one roof
34. 4.
Just
index
some
column
values
SQL>
select
*
from
bowie_stuff
where
processed
=
'NO';
Execution Plan
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 40 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| BOWIE_STUFF | 5 | 40 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | BOWIE_STUFF_I | 5 | | 3 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
540 bytes sent via SQL*Net to client
396 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5 rows processed
The most comprehensive Oracle applications & technology content under one roof
35. 4.
Just
index
some
column
values
However
in
11g
R2,
if
we
now
recreate
the
index
as
a
parFFoned
index
with
only
the
“useful”
porFon
of
the
index
usable
...
SQL>
drop
index
bowie_stuff_i;
Index
dropped.
SQL>
create
index
bowie_stuff_i
on
bowie_stuff(processed)
2
global
parFFon
by
range
(processed)
3
(parFFon
not_processed_part
values
less
than
('YES'),
4
parFFon
processed_part
values
less
than
(MAXVALUE))
5
unusable;
Index
created.
SQL>
alter
index
bowie_stuff_i
rebuild
parFFon
not_processed_part;
Index
altered.
The most comprehensive Oracle applications & technology content under one roof
36. 4.
Just
index
some
column
values
We
now
only
use
a
fracFon
of
the
storage
for
the
index
and
the
“useful”
porFon
of
the
indexed
data
is
just
a
single
leaf
block
in
size
...
SQL>
select
index_name,
parFFon_name,
leaf_blocks
from
dba_ind_parFFons
where
index_name
=
'BOWIE_STUFF_I';
INDEX_NAME PARTITION_NAME LEAF_BLOCKS
-------------------- -------------------- -----------
BOWIE_STUFF_I PROCESSED_PART 0
BOWIE_STUFF_I NOT_PROCESSED_PART 1
SQL>
select
segment_name,
parFFon_name,
blocks
from
dba_segments
where
segment_name
=
'BOWIE_STUFF_I';
SEGMENT_NAME PARTITION_NAME BLOCKS
-------------------- -------------------- ----------
BOWIE_STUFF_I NOT_PROCESSED_PART 8
The most comprehensive Oracle applications & technology content under one roof
37. 4.
Just
index
some
column
values
SQL>
select
*
from
bowie_stuff
where
processed
=
'NO';
Execution Plan
--------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 45 | 1 (0)| 00:00:01 | | |
| 1 | PARTITION RANGE SINGLE | | 5 | 45 | 1 (0)| 00:00:01 | 1 | 1 |
| 2 | TABLE ACCESS BY INDEX ROWID| BOWIE_STUFF | 5 | 45 | 1 (0)| 00:00:01 | | |
|* 3 | INDEX RANGE SCAN | BOWIE_STUFF_I | 5 | | 1 (0)| 00:00:01 | 1 | 1 |
--------------------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4 consistent gets
0 physical reads
0 redo size
542 bytes sent via SQL*Net to client
395 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
5 rows processed
Note:
The
query
itself
is
also
more
efficient
with
consistent
gets
reduced
from
6
down
to
4
...
The most comprehensive Oracle applications & technology content under one roof
38. 5.
100%
Index
SelecFvity
• Generally
indexes
only
process
a
“small”
%
of
overall
data
• Cheaper
to
use
a
Full
Table
Scan
to
process
a
“high”
%
of
data
• Common
belief
that
if
returning
more
than
‘x’%,
indexes
are
ignored
by
CBO
• However,
indexes
can
be
most
efficient
access
path
when
returning
anything
up
to
100%
of
data
The most comprehensive Oracle applications & technology content under one roof
39. 5.
100%
Index
SelecFvity
SQL>
create
table
ziggy
(id
number,
name
varchar2(30));
Table
created.
SQL>
insert
into
ziggy
select
rownum,
'ZIGGY
STARDUST'
from
dual
connect
by
level
<=
1000000;
1000000
rows
created.
SQL>
commit;
Commit
complete.
SQL>
delete
ziggy
where
id
between
1
and
999000;
999000
rows
deleted.
SQL>
commit;
Commit
complete.
SQL>
create
index
ziggy_id_i
on
ziggy(id);
Index
created.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>'BOWIE',
tabname=>'ZIGGY',
cascade=>
true,
esFmate_percent=>null,
method_opt=>
'FOR
ALL
COLUMNS
SIZE
1');
The most comprehensive Oracle applications & technology content under one roof
40. 5.
100%
Index
SelecFvity
SQL>
select
id
from
ziggy
where
id
>
1;
1000
rows
selected.
Execution Plan
-----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 5000 | 3 (0)| 00:00:01 |
|* 1 | INDEX FAST FULL SCAN| ZIGGY_ID_I | 1000 | 5000 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------
If
all
columns
of
interest
can
be
found
within
an
index,
an
Index
Fast
Full
Scan
can
treat
the
index
as
a
“skinny”
table.
The most comprehensive Oracle applications & technology content under one roof
41. 5.
100%
Index
SelecFvity
SQL>
select
*
from
ziggy
where
id
>
1;
1000
rows
selected.
Execution Plan
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000 | 20000 | 8 (0)|00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| ZIGGY | 1000 | 20000 | 8 (0)|00:00:01 |
|* 2 | INDEX RANGE SCAN | ZIGGY_ID_I | 1000 | | 4 (0)|00:00:01 |
------------------------------------------------------------------------------------------
However,
because
the
table
is
so
fragmented,
the
CBO
is
using
the
index
to
access
the
table
and
retrieve
100%
of
the
rows
...
The most comprehensive Oracle applications & technology content under one roof
42. 5.
100%
Index
SelecFvity
SQL>
CREATE
TABLE
big_dwh_table_2
AS
SELECT
*
FROM
big_dwh_table
ORDER
BY
total_sales;
Table
created.
SQL>
CREATE
INDEX
big_dwh_album_id_i
ON
big_dwh_table(album_id);
Index
created.
SQL>
CREATE
INDEX
big_dwh_2_album_id_i
ON
big_dwh_table_2(album_id);
Index
created.
SQL>
SELECT
index_name,
leaf_blocks,
clustering_factor
FROM
user_indexes
WHERE
index_name
like
'BIG_DWH%ALBUM_ID_I';
INDEX_NAME LEAF_BLOCKS CLUSTERING_FACTOR
------------------------------ ----------- -----------------
BIG_DWH_2_ALBUM_ID_I 2090 990282
BIG_DWH_ALBUM_ID_I 2090 4957
The most comprehensive Oracle applications & technology content under one roof
43. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
FROM
big_dwh_table
WHERE
album_id
BETWEEN
10
AND
20
ORDER
BY
id;
1100
rows
selected.
Execution Plan
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows| Bytes |Cost(%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | |1200 | 36000 | 12 (9)|00:00:01 |
| 1 | SORT ORDER BY | |1200 | 36000 | 12 (9)|00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE |1200 | 36000 | 11 (0)|00:00:01 |
|* 3 | INDEX RANGE SCAN | BIG_DWH_ALBUM_ID_I |1200 | | 5 (0)|00:00:01 |
----------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
11 consistent gets
4 physical reads
0 redo size
30759 bytes sent via SQL*Net to client
396 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1100 rows processed
The most comprehensive Oracle applications & technology content under one roof
44. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
FROM
big_dwh_table
WHERE
album_id
BETWEEN
10
AND
20
ORDER
BY
album_id;
1100 rows selected.
Execution Plan
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost(%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1200 | 36000 | 11 (0)|00:00:01|
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE | 1200 | 36000 | 11 (0)|00:00:01|
|* 2 | INDEX RANGE SCAN | BIG_DWH_ALBUM_ID_I | 1200 | | 5 (0)|00:00:01|
----------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
13 consistent gets
0 physical reads
0 redo size
30759 bytes sent via SQL*Net to client
396 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1100 rows processed
The most comprehensive Oracle applications & technology content under one roof
45. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
FROM
big_dwh_table
WHERE
album_id
BETWEEN
1
AND
10000
ORDER
BY
album_id;
Execution Plan
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost(%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 28M|7145 (2)|00:00:48|
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE | 1000K| 28M|7145 (2)|00:00:48|
|* 2 | INDEX FULL SCAN | BIG_DWH_ALBUM_ID_I | 1000K| |2130 (2)|00:00:15|
---------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
139919 consistent gets
0 physical reads
0 redo size
37476458 bytes sent via SQL*Net to client
733722 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000000 rows processed
The most comprehensive Oracle applications & technology content under one roof
46. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
FROM
big_dwh_table
ORDER
BY
album_id;
1000000
rows
selected.
Execution Plan
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 28M| | 7699 (4)| 00:00:51 |
| 1 | SORT ORDER BY | | 1000K| 28M| 91M| 7699 (4)| 00:00:51 |
| 2 | TABLE ACCESS FULL| BIG_DWH_TABLE | 1000K| 28M| | 555 (10)| 00:00:04 |
--------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
43 recursive calls
5 db block gets
4954 consistent gets
5432 physical reads
0 redo size
37476485 bytes sent via SQL*Net to client
733722 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client
0 sorts (memory)
1 sorts (disk)
1000000 rows processed
The most comprehensive Oracle applications & technology content under one roof
47. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
FROM
big_dwh_table
WHERE
album_id
IS
NOT
NULL
ORDER
BY
album_id;
1000000 rows selected.
Execution Plan
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost(%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 28M|7145 (2)|00:00:48|
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE | 1000K| 28M|7145 (2)|00:00:48|
|* 2 | INDEX FULL SCAN | BIG_DWH_ALBUM_ID_I | 1000K| |2130 (2)|00:00:15|
----------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
139919 consistent gets
7040 physical reads
0 redo size
37476493 bytes sent via SQL*Net to client
733722 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000000 rows processed
The most comprehensive Oracle applications & technology content under one roof
48. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
FROM
big_dwh_table_2
WHERE
album_id
IS
NOT
NULL
ORDER
BY
album_id;
1000000 rows selected.
Execution Plan
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 28M| | 7704 (4)| 00:00:51 |
| 1 | SORT ORDER BY | | 1000K| 28M| 91M| 7704 (4)| 00:00:51 |
|* 2 | TABLE ACCESS FULL| BIG_DWH_TABLE_2 | 1000K| 28M| | 561 (11)| 00:00:04 |
----------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
44 recursive calls
18 db block gets
4979 consistent gets
7669 physical reads
0 redo size
36721186 bytes sent via SQL*Net to client
733722 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client
0 sorts (memory)
1 sorts (disk)
1000000 rows processed
The most comprehensive Oracle applications & technology content under one roof
49. 5.
100%
Index
SelecFvity
SQL>
SELECT
/*+
index(a)
*/
*
from
big_dwh_table_2
a
WHERE
album_id
IS
NOT
NULL
ORDER
BY
album_id;
1000000 rows selected.
Execution Plan
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost (%CPU)|Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1000K| 28M| 993K (1)|01:49:31 |
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE_2 | 1000K| 28M| 993K (1)|01:49:31 |
|* 2 | INDEX FULL SCAN | BIG_DWH_2_ALBUM_ID_I | 1000K| | 2130 (2)|00:00:15 |
-------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1059553 consistent gets
7065 physical reads
0 redo size
36720213 bytes sent via SQL*Net to client
733722 bytes received via SQL*Net from client
66668 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1000000 rows processed
The most comprehensive Oracle applications & technology content under one roof
50. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
from
big_dwh_table_2
WHERE
album_id
BETWEEN
1
AND
15
ORDER
BY
album_id;
1500 rows selected.
Execution Plan
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost(%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1500 | 45000 | 1297 (5)| 00:00:07|
| 1 | SORT ORDER BY | | 1500 | 45000 | 1297 (5)| 00:00:07|
|* 2 | TABLE ACCESS FULL| BIG_DWH_TABLE_2 | 1500 | 45000 | 1296 (4)| 00:00:07|
------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4981 consistent gets
0 physical reads
0 redo size
54139 bytes sent via SQL*Net to client
1485 bytes received via SQL*Net from client
101 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1500 rows processed
The most comprehensive Oracle applications & technology content under one roof
51. 5.
100%
Index
SelecFvity
SQL>
SELECT
*
from
big_dwh_table_2
WHERE
album_id
BETWEEN
1
AND
13
ORDER
BY
album_id;
1300 rows selected.
Execution Plan
-------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |Cost(%CPU)| Time |
-------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1300| 39000 |1295 (1)| 00:00:07 |
| 1 | TABLE ACCESS BY INDEX ROWID| BIG_DWH_TABLE_2 | 1300| 39000 |1295 (1)| 00:00:07 |
|* 2 | INDEX RANGE SCAN | BIG_DWH_2_ALBUM_ID_I | 1300| | 5 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1385 consistent gets
60 physical reads
0 redo size
47048 bytes sent via SQL*Net to client
1342 bytes received via SQL*Net from client
88 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1300 rows processed
The most comprehensive Oracle applications & technology content under one roof
52. 6.
BLEVEL
1
=>
BLEVEL
2
Careful
of
indexes
increasing
from
BLEVEL
1
to
2.
Following
demo
11.2.0.1
with
8K
block
size.
SQL>
create
table
major_tom
(id
number,
code
number,
name
varchar2(30));
Table
created.
SQL>
create
index
major_tom_i
on
major_tom(id);
Index
created.
SQL>
insert
into
major_tom
select
rownum,
mod(rownum,100),
'GROUND
CONTROL'
from
dual
connect
by
level
<=336000;
336000
rows
created.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>null,
tabname=>
'MAJOR_TOM',
cascade=>
true,
esFmate_percent=>null,
method_opt=>'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
SQL>
select
blevel,
leaf_blocks,
num_rows,
clustering_factor
from
dba_indexes
wh
ere
index_name='MAJOR_TOM_I';
BLEVEL LEAF_BLOCKS NUM_ROWS CLUSTERING_FACTOR
---------- ----------- ---------- -----------------
1 671 336000 1296
The most comprehensive Oracle applications & technology content under one roof
53. 6.
BLEVEL
1
=>
BLEVEL
2
SQL>
select
*
from
major_tom
where
id
=
42;
Execution Plan
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 23 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MAJOR_TOM | 1 | 23 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | MAJOR_TOM_I | 1 | | 1 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
4 consistent gets
0 physical reads
0 redo size
531 bytes sent via SQL*Net to client
395 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
6 sorts (memory)
0 sorts (disk)
1 rows processed
When
BLEVEL
=
1,
CBO
ignores
the
BLEVEL
from
its
calculaFons
The most comprehensive Oracle applications & technology content under one roof
54. 6.
BLEVEL
1
=>
BLEVEL
2
Let’s
create
another
table,
with
a
CODE
column
that’s
has
100
occurrences
of
each
value
...
SQL>
create
table
ziggy
(id
number,
code
number,
name
varchar2(30));
Table
created.
SQL>
insert
into
ziggy
select
rownum,
mod(rownum,10000),
'ZIGGY
STARDUST'
from
dual
connect
by
level
<=
1000000;
1000000
rows
created.
SQL>
commit;
Commit
complete.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>null,
tabname=>
'ZIGGY',
cascade=>
true,
esFmate_percent=>null,
method_opt=>'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
The most comprehensive Oracle applications & technology content under one roof
55. 6.
BLEVEL
1
=>
BLEVEL
2
SQL>
select
*
from
ziggy
z,
major_tom
m
where
z.id
=
m.id
and
z.code
in
(42,
4242);
68
rows
selected.
Execution Plan
--------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200 | 9400 | 1372 (2)| 00:00:17 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 200 | 9400 | 1372 (2)| 00:00:17 |
|* 3 | TABLE ACCESS FULL | ZIGGY | 200 | 4800 | 1105 (2)| 00:00:14 |
|* 4 | INDEX RANGE SCAN | MAJOR_TOM_I | 1 | | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| MAJOR_TOM | 1 | 23 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------
CBO
choose
a
Nest
Loop
as
the
cost
of
accessing
the
MAJOR_TOM
table
100
Fmes
via
the
index
is
relaFvely
inexpensive
The most comprehensive Oracle applications & technology content under one roof
56. 6.
BLEVEL
1
=>
BLEVEL
2
Let’s
just
add
another
500
rows
(or
0.15%
of
data)
SQL>
insert
into
major_tom
select
rownum+336000,
mod(rownum,100),
'GROUND
CONTROL'
from
dual
connect
by
level
<=500;
500
rows
created.
SQL>
commit;
Commit
complete.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>null,
tabname=>
'MAJOR_TOM',
cascade=>
true,
esFmate_percent=>null,
method_opt=>'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
SQL>
select
blevel,
leaf_blocks,
num_rows,
clustering_factor
from
dba_indexes
where
index_name
=
'MAJOR_TOM_I';
BLEVEL LEAF_BLOCKS NUM_ROWS CLUSTERING_FACTOR
---------- ----------- ---------- -----------------
2 672 336500 1298
Leaf
blocks
has
just
gone
up
by
just
1
but
it’s
enough
to
increase
the
BLEVEL
to
2
...
The most comprehensive Oracle applications & technology content under one roof
57. 6.
BLEVEL
1
=>
BLEVEL
2
SQL>
select
*
from
major_tom
where
id
=
42;
Execution Plan
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 23 | 4 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| MAJOR_TOM | 1 | 23 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | MAJOR_TOM_I | 1 | | 3 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Statistics
----------------------------------------------------------
1 recursive calls
0 db block gets
5 consistent gets
0 physical reads
0 redo size
531 bytes sent via SQL*Net to client
395 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
The
cost
of
accessing
1
row
has
gone
up
by
2
as
the
BLEVEL
is
no
longer
ignored
by
the
CBO
...
The most comprehensive Oracle applications & technology content under one roof
58. 6.
BLEVEL
1
=>
BLEVEL
2
SQL>
select
*
from
ziggy
z,
major_tom
m
where
m.id
=
z.id
and
z.code
in
(42,
4242);
68
rows
selected.
Execution Plan
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 200 | 9400 | 1485 (2)| 00:00:18 |
|* 1 | HASH JOIN | | 200 | 9400 | 1485 (2)| 00:00:18 |
|* 2 | TABLE ACCESS FULL| ZIGGY | 200 | 4800 | 1105 (2)| 00:00:14 |
| 3 | TABLE ACCESS FULL| MAJOR_TOM | 336K| 7558K| 378 (1)| 00:00:05 |
--------------------------------------------------------------------------------
Nested
Loop
costs
(previously
1372)
have
gone
up
considerably
as
the
cost
has
gone
up
by
2
for
each
iteraFon
of
the
loop.
Hash
Join
and
FTS
now
selected
by
the
CBO
even
though
overall
size
of
index
is
similar.
Index
BLEVEL
can
potenFally
toggle
between
1
and
2
and
result
in
variable
performance
if
size
is
borderline
and
index
frequently
rebuilt
due
to
some
deleFons.
The most comprehensive Oracle applications & technology content under one roof
59. 7.
Why
Unique
Indexes
Are
Beyer
• Since
Oracle8,
Non-‐Unique
Indexes
Can
Police
Primary
and
Unique
Key
Constraints
• Non-‐Unique
Indexes
Necessary
For
Deferrable
and
NoValidate
Constraints
• Non-‐Unique
Indexes
are
not
automaFcally
dropped
when
constraint
is
dropped
or
disabled
• However,
Unique
Indexes
are
not
outdated
and
are
generally
“beyer”
...
The most comprehensive Oracle applications & technology content under one roof
60. 7.
Why
Unique
Indexes
Are
Beyer
SQL>
CREATE
TABLE
index_size_test
(id
NUMBER,
value
VARCHAR2(100)
CONSTRAINT
index_size_test_pk_1
PRIMARY
KEY);
Table
created.
SQL>
INSERT
INTO
index_size_test
VALUES
(1,
'DAVID
BOWIE');
1
row
created.
SQL>
COMMIT;
Commit
complete.
SQL>
SELECT
header_file,
header_block+1
FROM
dba_segments
WHERE
segment_name
=
'INDEX_SIZE_TEST_PK_1';
HEADER_FILE HEADER_BLOCK+1
----------- --------------
5 138370
SQL>
ALTER
SYSTEM
DUMP
DATAFILE
5
BLOCK
138370;
System
altered.
The most comprehensive Oracle applications & technology content under one roof
61. 7.
Why
Unique
Indexes
Are
Beyer
row#0[8016]
flag:
-‐-‐-‐-‐-‐-‐,
lock:
2,
len=20,
data:(6):
01
42
1c
7a
00
00
col
0;
len
11;
(11):
44
41
56
49
44
20
42
4f
57
49
45
SQL>
ALTER
TABLE
index_size_test
DROP
PRIMARY
KEY;
Table
altered.
SQL>
CREATE
INDEX
index_size_test_pk_2
ON
index_size_test(value);
Index
created.
SQL>
ALTER
TABLE
index_size_test
ADD
PRIMARY
KEY
(value);
Table
altered.
row#0[8015]
flag:
-‐-‐-‐-‐-‐-‐,
lock:
0,
len=21
col
0;
len
11;
(11):
44
41
56
49
44
20
42
4f
57
49
45
col
1;
len
6;
(6):
01
42
1c
7a
00
00
The most comprehensive Oracle applications & technology content under one roof
62. 7.
Why
Unique
Indexes
Are
Beyer
SQL>
CREATE
TABLE
test1
AS
SELECT
rownum
id
FROM
dual
CONNECT
BY
LEVEL
<=
1000000;
Table
created.
SQL>
CREATE
INDEX
non_unique_idx
ON
test1(id)
PCTFREE
0;
Index
created.
SQL>
CREATE
TABLE
test2
AS
SELECT
rownum
id
FROM
dual
CONNECT
BY
LEVEL
<=
1000000;
Table
created.
SQL>
CREATE
UNIQUE
INDEX
non_unique_idx
ON
test2(id)
PCTFREE
0;
Index
created.
SQL>
exec
dbms_stats.gather_index_stats(ownname=>'BOWIE',
indname=>
'NON_UNIQUE_IDX',
esFmate_percent=>
null);
PL/SQL
procedure
successfully
completed.
SQL>
exec
dbms_stats.gather_index_stats(ownname=>'BOWIE',
indname=>'UNIQUE_IDX',
esFmate_percent=>
null);
PL/SQL
procedure
successfully
completed.
The most comprehensive Oracle applications & technology content under one roof
63. 7.
Why
Unique
Indexes
Are
Beyer
SQL>
SELECT
index_name,
blevel,
leaf_blocks,
num_rows
FROM
dba_indexes
WHERE
index_name
IN
('NON_UNIQUE_IDX',
'UNIQUE_IDX');
INDEX_NAME BLEVEL LEAF_BLOCKS NUM_ROWS
-------------- ------ ----------- --------
NON_UNIQUE_IDX 2 1999 1000000
UNIQUE_IDX 2 1875 1000000
The
Non-‐Unique
Index
has
used
an
extra
124
blocks
or
approximately
6.6%
more
blocks
than
the
Unique
Index
The most comprehensive Oracle applications & technology content under one roof
64. 7.
Why
Unique
Indexes
Are
Beyer
Unique
indexes
have
less
latching
and
CPU
related
overheads
...
SQL>
CREATE
TABLE
ziggy
(id
NUMBER,
name
VARCHAR2(30));
Table
created.
SQL>
INSERT
into
ziggy
SELECT
rownum,
'Bowie'
FROM
dual
CONNECT
BY
level
<=1000;
1000
rows
created.
SQL>
COMMIT;
Commit
complete.
SQL>
ALTER
TABLE
ziggy
ADD
PRIMARY
KEY
(id);
Table
altered.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>NULL,
tabname=>'ZIGGY',
esFmate_percent=>
NULL,
method_opt=>
'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
The most comprehensive Oracle applications & technology content under one roof
65. 7.
Why
Unique
Indexes
Are
Beyer
In
one
session,
run
the
following
a
couple
of
Fmes
to
ensure
no
recursive
SQL:
SQL>
SELECT
*
FROM
ziggy
WHERE
id
=
10;
ID NAME
----- -----------------------
10 Bowie
In
other
session,
run
the
following
(where
SID
=
the
sid
of
the
other
session)
before
and
a‚er
an
execuFon
of
the
above
select
statement
in
the
other
session.
SQL>
SELECT
n.name,
s.value
FROM
v$sesstat
s,
v$statname
n
WHERE
s.staFsFc#
=
n.staFsFc#
AND
s.sid
=
123
AND
n.name
like
'consistent%';
The most comprehensive Oracle applications & technology content under one roof
66. 7.
Why
Unique
Indexes
Are
Beyer
NAME
VALUE
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
consistent
gets
17703
consistent
gets
from
cache
17703
consistent
gets
-‐
examinaFon
10536
NAME
VALUE
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
consistent
gets
17706
consistent
gets
from
cache
17706
consistent
gets
-‐
examinaFon
10539
Note
that
consistent
gets
increases
by
3,
consistent
gets
from
cache
increases
by
3,
consistent
gets
-‐
examinaFon
increases
by
3
(1
for
the
index
root
block,
1
for
the
index
leaf
block
and
1
for
the
table
block).
That’s
a
total
of
3
CRs
and
3
latches
(as
all
CRs
are
the
cheaper
‘examinaFons’
which
only
require
1
latch
each)
The most comprehensive Oracle applications & technology content under one roof
67. 7.
Why
Unique
Indexes
Are
Beyer
Now
the
same
test
but
this
Fme
with
a
non-‐unique
index
...
SQL>
ALTER
TABLE
ziggy
DROP
PRIMARY
KEY;
Table
altered.
SQL>
ALTER
TABLE
ziggy
ADD
PRIMARY
KEY
(id)
USING
INDEX
2
(CREATE
INDEX
ziggy_id_i
ON
ziggy(id));
Table
altered.
SQL>
exec
dbms_stats.gather_table_stats(ownname=>NULL,
tabname=>'ZIGGY',
esFmate_percent=>
NULL,
method_opt=>
'FOR
ALL
COLUMNS
SIZE
1');
PL/SQL
procedure
successfully
completed.
The most comprehensive Oracle applications & technology content under one roof
68. 7.
Why
Unique
Indexes
Are
Beyer
NAME
VALUE
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
consistent
gets
18504
consistent
gets
from
cache
18504
consistent
gets
-‐
examinaFon
10949
NAME
VALUE
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐-‐
consistent
gets
18508
consistent
gets
from
cache
18508
consistent
gets
-‐
examinaFon
10950
Note
that
consistent
gets
increases
by
4
(not
3),
consistent
gets
from
cache
increases
by
4
(not
3),
consistent
gets
-‐
examinaFon
increases
by
only
1(not
3).
In
summary,
there
are
4
not
3
CRs
with
only
the
root
block
acquired
via
a
1
latch
examinaFon
CR,
the
other
3
CRs
are
‘full’
2
latch
gets
which
is
a
total
of
7
latches
(vs.
only
3
for
the
equivalent
unique
index).
The most comprehensive Oracle applications & technology content under one roof
69. 7.
Why
Unique
Indexes
Are
Beyer
Create
table
with
a
PK
and
ayempt
to
insert
duplicate
PK
rows
...
SQL>
create
table
radiohead
(id
number
constraint
radiohead_pk_i
primary
key
using
index
(create
unique
index
radiohead_pk_i
on
radiohead(id)),
name
varchar2(20));
Table
created.
SQL>
select
index_name,
uniqueness,
table_name
from
dba_indexes
where
index_name
=
'RADIOHEAD_PK_I';
INDEX_NAME UNIQUENES TABLE_NAME
------------------------------ --------- ------------------------------
RADIOHEAD_PK_I UNIQUE RADIOHEAD
SQL>
insert
into
radiohead
select
rownum,
'OK
COMPUTER'
from
dual
connect
by
level
<=
10;
10
rows
created.
SQL>
commit;
Commit
complete.
SQL>
insert
into
radiohead
select
rownum,
'OK
COMPUTER'
from
dual
connect
by
level
<=
12;
insert
into
radiohead
select
rownum,
'OK
COMPUTER'
from
dual
connect
by
level
<=
12
*
ERROR
at
line
1:
ORA-‐00001:
unique
constraint
(BOWIE.RADIOHEAD_PK_I)
violated
The most comprehensive Oracle applications & technology content under one roof
70. 7.
Why
Unique
Indexes
Are
Beyer
With
the
new
Oracle11g
Rel.
1
hint,
duplicate
violaFon
rows
are
automaFcally
ignored.
/*+
ignore_row_on_dupkey_index(radiohead,radiohead_pk_i)
*/
into
radiohead
select
rownum,
'OK
COMPUTER'
from
dual
SQL>
insert
connect
by
level
<=
12;
2
rows
created.
SQL>
insert
/*+
ignore_row_on_dupkey_index(radiohead(id))
*/
into
radiohead
select
rownum,
'OK
COMPUTER'
from
dual
connect
by
level
<=
13;
1
row
created.
SQL>
commit;
Commit
complete.
SQL>
select
*
from
radiohead;
ID NAME
---------- --------------------
1 OK COMPUTER
2 OK COMPUTER
3 OK COMPUTER
4 OK COMPUTER
5 OK COMPUTER
6 OK COMPUTER
7 OK COMPUTER
8 OK COMPUTER
9 OK COMPUTER
10 OK COMPUTER
11 OK COMPUTER
12 OK COMPUTER
13 OK COMPUTER
13 rows selected.
The most comprehensive Oracle applications & technology content under one roof