3. Topics
• Common
ways
to
rewrite
SQL
to
make
it
perform
beFer
and
more
consistently
• How
to
easily
idenHfy
&
test
your
SQL
• How
and
when
to
index
– AddiHons
or
modificaHons
to
provide
best
soluHon
– Best
choice
of
columns
and
in
what
order
– Trade-‐offs
for
determining
the
"best"
index
20. Name
your
SQL
SELECT /* kmtest */ …
FROM tab …
SELECT /*+ qb_name (sql42) */ …
FROM tab …
Comments
stored
with
full
SQL
in
the
SQL_TEXT
column
in
v$sql,
v$sqltext,
dba_hist_sqltext.
QBLOCK_NAME
stored
in
v$sql_plan,
v$sql_plan_staHsHcs_all,
dba_hist_sql_plan.
27. @fsx kmtest!
!
!
col sql_id new_value r_sqlid!
col child_number new_value r_childno!
!
!
SELECT /* NOVIEW */ !
! ! sql_id, child_number ...!
FROM gv$sql s!
WHERE sql_text like '%&&1%'!
AND sql_text not like '%NOVIEW%'!
AND sql_text not like 'BEGIN :sql_%'!
ORDER BY 1 ;!
28. @dplan!
SELECT *!
FROM table(dbms_xplan.display_cursor (!
! ! SQL_ID => '&r_sqlid', !
! ! CURSOR_CHILD_NO => NVL(&r_childno,0), !
! ! FORMAT => 'ALLSTATS LAST')) !
WHERE '&r_sqlid' IS NOT NULL ;!
@rsm!
SELECT dbms_sqltune.report_sql_monitor(!
! ! SQL_ID => '&r_sqlid',!
! ! TYPE => 'HTML') !
FROM dual!
WHERE '&r_sqlid' IS NOT NULL ;!
29. How
do
you
know
when
refactoring
SQL
is
the
best
opHon?
31. Why
You
Should
Refactor
• You
know
your
stuff
best
(or
you
should)
• Always
filter
early
• Defines
your
expectaHons
• K.I.S.S.
• The
opHmizer
might
not
be
able
to
34. This
SELECT b.*
FROM a, b
WHERE a.col1 = b.col1
AND b.col2 = <condition>
Becomes
SELECT b.*
FROM b
WHERE b.col2 = <condition>
AND EXISTS (SELECT null
FROM a WHERE a.col1 = b.col1)
36. This
AND tab1.col1 = tab2.col1 (+)
AND tab2.col2 = <condition>
Becomes
AND tab1.col1 = tab2.col1
AND tab2.col2 = <condition>
Because
the
condiHon
would
be
null
for
the
outer
joined
row,
so
the
predicate
could
never
be
true.
37. Look
for
repeated
use
of
same
tables
and
predicates.
38. SELECT rite.event_name, count(*)
FROM riffs.rf_order ro, riffs.rf_order_item roi,
riffs.rf_item_transaction rit,
riffs.rf_item_transaction_event rite
WHERE ro.is_test = '0'
AND ro.order_id = roi.order_id
AND roi.order_item_id = rit.order_item_id
AND roi.order_id = rit.order_id
AND rit.transaction_id = rite.transaction_id
AND (rite.event_name >'AUTHORIZED' OR rite.event_name <'AUTHORIZED')
GROUP BY rite.event_name
UNION ALL
SELECT 'TRANSACTION_INITIATED', count(*)
FROM
(SELECT count(*)
FROM riffs.rf_order ro, riffs.rf_order_item roi,
riffs.rf_item_transaction rit,
riffs.rf_item_transaction_event rite
WHERE ro.is_test = '0'
AND ro.order_id = roi.order_id
AND roi.order_item_id = rit.order_item_id
AND roi.order_id = rit.order_id
AND rit.transaction_id = rite.transaction_id
AND rite.event_name = 'AUTHORIZED'
GROUP BY substr(rit.TRANSACTION_ID,1,INSTR(rit.TRANSACTION_ID,'_')-1))
39. Look
for
simple
predicates
ORed
with
other
predicates
in
ranges.
40. This
col1 > <condition>
OR col2 > <condition>
Becomes
col1 > <condition>
UNION / UNION ALL
AND col2 > <condition>
Because
a
row
could
not
be
rejected
when
one
predicate
is
false
without
checking
the
other
predicates.
41. Look
for
DISTINCT/UNION
to
remove
duplicates.
Consider
using
IN
or
EXISTS
instead.
43. Simple
View
Merging
is transformed into
Merged
automaHcally
as
it
is
deemed
“always
beFer”
for
the
opHmizer
to
work
with
direct
joins.
44. Complex
View
Merging
is transformed into
“Complex”
due
to
GROUP
BY.
CVM
can
also
be
done
when
using
DISTINCT
or
outer
join.
45. Filter
Push-‐Down
is transformed into
Purpose:
To
push
outer
query
predicates
into
view
to
perform
earlier
filtering.
46. Predicate
Move-‐Around
is transformed into
Purpose:
To
move
inexpensive
predicates
into
view
query
blocks
to
perform
earlier
filtering.
Can
generate
filter
predicates
based
on
transiHvity
or
funcHonal
dependencies.
47. Join
FactorizaHon
is transformed into
Combines
branches
of
UNION
/
UNION
ALL
that
join
a
common
table
in
order
to
reduce
#
of
accesses
to
that
table.
48. Understanding
how
the
opHmizer
transforms
queries
helps
you
write
beFer
SQL
and
understand
execuHon
plans.
50. For
many
years,
inadequate
indexing
has
been
the
most
common
cause
of
performance
disappointments.
–
Tapio
Lahdenmäki
51. Indexing
Problems
• Indexes
that
do
not
have
sufficient
columns
to
support
all
predicates
• Not
enough
indexes
present
– Numerous
single-‐column
but
few
mulH-‐column
• Indexes
with
the
right
columns
but
in
the
wrong
order
55. Inadequate
Index
SELECT !cust_id, cust_first_name!
FROM ! !customers!
!
WHERE ! !cust_last_name = 'Ruddy'!
AND ! ! !cust_city = 'Ede'!
ORDER BY cust_first_name ;!
Index
present
on
CUST_LAST_NAME,
CUST_FIRST_NAME
#
rows
in
table
=
55,500
56. Note
the
number
of
rows
that
are
thrown
away
in
step
1
(79).
57. Index
on
CUST_LAST_NAME,
CUST_CITY
No
throwaway
Index
on
CUST_CITY,
CUST_LAST_NAME,
CUST_FIRST_NAME,
CUST_ID
No
throwaway,
no
sort
58. Index
Design
Strategies
Columns
from
all
equality
predicates
in
any
order
Add
columns
in
the
order
used
in
ORDER
BY
Add
all
remaining
columns
from
column
list
in
any
order
• VolaHle
columns
at
end
(reduces
impact
on
updates)
59. Range
predicates
are
usually
placed
ater
1
and
2
star
columns.
Range
predicates
mean
3-‐star
indexes
aren't
possible.
The
2nd
star
is
usually
sacrificed
in
preference
of
a
more
selecHve
index.
61. An
index
that
contains
all
the
columns
referenced
in
the
WHERE
clause
is
a
semi-fat
index.
If
no
semi-‐fat
index
exists,
this
is
a
warning
flag
for
possible
performance
issues.
62. There
will
always
be
trade-‐offs.
Test
alternaHves
or
use
"worst
case"
formula
to
esHmate
response
Hmes.
63. The
key
to
determining
an
ideal
index
The
index
should
provide
adequate
enough
screening
to
minimize
table
accesses.
64. Recap
• Look
for
common
anH-‐paFerns
in
SQL
• Gather
enough
diagnosHc
data
to
know
where
the
problem
originates
• Learn
what
the
opHmizer
expects
(and
give
it
what
it
wants!)
• Think
about
your
indexing
strategy
• Design
indexes
for
opHmal
coverage
to
limit
table
accesses