SlideShare a Scribd company logo
1 of 45
How to “tune” a query
Thomas Kyte
http://asktom.oracle.com/
Agenda
• What single thing has the most impact on
performance and scalability?
• Doing It Wrong
• Doing It Still Wrong
• Tuning A Query
• Adding Information
• Connection Management
What Single
Thing?
The
Algorithm
The
Approach
Some Basic Math
• You write a routine that executes in 1ms
(1/1000th of a second)
• You have 5,000,000 things to process
• That is 5,000 seconds!
• That is 83 minutes!
• That is almost 1.5 hours!!!
• Oh, but we’ll do parallel threads in our
application…
Would you like to load
and validate one row
5,000,000 times
or load and validate
5,000,000 rows once?
create or replace procedure slow_by_slow
as
begin
for x in (select rowid rid, object_name
from t t_slow_by_slow)
loop
x.object_name := substr(x.object_name,2)
||substr(x.object_name,1,1);
update t
set object_name = x.object_name
where rowid = x.rid;
end loop;
end;
/
create or replace procedure bulk
as
type ridArray is table of rowid;
type onameArray is table
of t.object_name%type;
cursor c is select rowid rid, object_name
from t t_bulk;
l_rids ridArray;
l_onames onameArray;
N number := 100;
begin
open c;
loop
fetch c bulk collect
into l_rids, l_onames limit N;
for i in 1 .. l_rids.count
loop
l_onames(i) := substr(l_onames(i),2)
||substr(l_onames(i),1,1);
end loop;
forall i in 1 .. l_rids.count
update t
set object_name = l_onames(i)
where rowid = l_rids(i);
exit when c%notfound;
end loop;
close c;
end;
update t
set object_name = substr(object_name,2) ||
substr(object_name,1,1);
create table new_table
as
select OWNER,
substr(object_name,2) || substr(object_name,1,1)
OBJECT_NAME,
SUBOBJECT_NAME, OBJECT_ID, DATA_OBJECT_ID, OBJECT_TYPE,
CREATED, LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY,
GENERATED, SECONDARY, NAMESPACE, EDITION_NAME
from t;
Method CPU units of time
Slow by Slow 495
Bulk 193 (39%)
Single SQL statement 91 (18%) *near order of magnitude
CTAS 28 (5%) *two orders of magnitude
The
Algorithm
The
Approach
Doing it wrong
Case 1
Code…
• Write as much code:
– As you have to
– But as little as you can…
• Think in SETS
• Use (really use – not just ‘use’) SQL
Begin
For x in ( select * from table@remote_db )
Loop
Insert into table ( c1, c2, … )
values ( x.c1, x.c2,… );
End loop;
End;
Insert into table (c1,c2,…)
select c1,c2,…. From table@remote_db
Insert into table (c1,c2,…)
select c1,c2,…. From table@remote_db
LOG ERRORS ( some_variable )
REJECT LIMIT UNLIMITED;
… code to handle errors
for tag some_variable …
More Code = More Bugs
Less Code = Less Bugs
• Always look at the procedural code and ask yourself
“is there a set based way to do this algorithm”
– For example …
insert into t ( .... )
select EMPNO, STATUS_DATE, ....
from t1, t2, t3, t4, ....
where ....;
loop
delete from t
where (EMPNO,STATUS_DATE)
in ( select EMPNO,
min(STATUS_DATE)
from t
group by EMPNO
having count(1) > 1 );
exit when sql%rowcount = 0;
end loop;
More Code = More Bugs
Less Code = Less Bugs
insert into t ( .... )
select EMPNO, STATUS_DATE, ....
from t1, t2, t3, t4, ....
where ....;
loop
delete from t
where (EMPNO,STATUS_DATE)
in ( select EMPNO,
min(STATUS_DATE)
from t
group by EMPNO
having count(1) > 1 );
exit when sql%rowcount = 0;
end loop;
For any set of records with
more than one EMPNO,
remove rows with the oldest
STATUS_DATE.
Additionally – If the last set of
EMPNO records all have the
same STATUS_DATE, remove
them all.
More Code = More Bugs
Less Code = Less Bugs
EMPNO STATUS_DATE
------- ------------
1 01-jan-2009
1 15-jun-2009
1 01-sep-2009
…
1000 01-feb-2009
1000 22-aug-2009
1000 10-oct-2009
1000 10-oct-2009(1,01-jan-2009,3) (1001,01-feb-2009,4)
EMPNO STATUS_DATE
------- ------------
1 01-jan-2009
1 15-jun-2009
1 01-sep-2009
…
1000 01-feb-2009
1000 22-aug-2009
1000 10-oct-2009
1000 10-oct-2009
EMPNO STATUS_DATE
------- ------------
1 01-jan-2009
1 15-jun-2009
1 01-sep-2009
…
1000 01-feb-2009
1000 22-aug-2009
1000 10-oct-2009
1000 10-oct-2009(1,15-jun-2009,2) (1001,22-aug-2009,3)
EMPNO STATUS_DATE
------- ------------
1 01-jan-2009
1 15-jun-2009
1 01-sep-2009
…
1000 01-feb-2009
1000 22-aug-2009
1000 10-oct-2009
1000 10-oct-2009(1001,22-aug-2009,2)
EMPNO STATUS_DATE
------- ------------
1 01-sep-2009
…
insert /* APPEND */ into t ( .... )
select EMPNO, STATUS_DATE, ......
from ( select EMPNO, STATUS_DATE, .... ,
max(STATUS_DATE)
OVER ( partition by EMPNO ) max_sd,
count(EMPNO)
OVER ( partition by EMPNO,STATUS_DATE ) cnt
from t1, t2, t3, t4, …
where … )
where STATUS_DATE = max_sd
and cnt = 1;
More Code = More Bugs
Less Code = Less Bugs
• This was a data warehouse load (load 2-3-4 times the data you want,
then delete? Ouch)
• It was wrong – procedural code is no easier to understand than set based
code, documentation is key
/* Load table t using history tables. History tables have
multiple records per employee. We need to keep the
history records for each employee that have the maximum
status date for that employee. We do that by computing
the max(status_date) for each employee (partition by EMPNO
finding max(status_date) and keeping only the records such
that the status_date for that record = max(status_date)
for all records with same empno */
insert /* APPEND */ into t ( .... )
select EMPNO, STATUS_DATE, ......
from ( select EMPNO, STATUS_DATE, .... ,
max(STATUS_DATE)
OVER ( partition by EMPNO ) max_sd
from t1, t2, t3, t4, …
where … )
where STATUS_DATE = max_sd;
More Code = More Bugs
Less Code = Less Bugs
/* Load table t using history tables. History tables have
multiple records per employee. We need to keep the
history records for each employee that have the maximum
status date for that employee. We do that by numbering
each history record by empno, keeping only the records such
that it is the first record for a EMPNO after sorting by
status_date desc. REALIZE: this is not deterministic if
there are two status_dates that are the same for a given
employee! */
insert /* APPEND */ into t ( .... )
select EMPNO, STATUS_DATE, ......
from ( select EMPNO, STATUS_DATE, .... ,
row_number() OVER ( partition by EMPNO
order by STATUS_DATE desc ) rn
from t1, t2, t3, t4, …
where … )
where rn=1;
More Code = More Bugs
Less Code = Less Bugs
Doing it wrong
Case 2
Use PL/SQL constructs only when SQL cannot do it
• Another coding ‘technique’ I see frequently:
• The developer did not want to “burden” the database
with a join
For a in ( select * from t1 )
Loop
For b in ( select * from t2
where t2.key = a.key )
Loop
For c in ( select * from t3
where t3.key = b.key )
Loop
…
Create or replace function get_xxxx( … ) return …
• Be fearful of functions that start with GET_
• You cannot tune them, they are already tuned
– They use the simplest of SQL
Create or replace function get_emp_details
( p_id in number ) return emp_record
As
l_rec emp_record;
l_id number;
Begin
select max(id2)
into l_id
from t1
where id = p_id;
Create or replace function get_emp_details
( p_id in number ) return emp_record
As
l_rec emp_record;
l_id number;
Begin
select max(id2)
into l_id
from t1
where id = p_id;
select first_name, last_name
into l_rec.first_name, l_rec.last_name
from t2
where id = l_id;
select first_name, last_name
into l_rec.first_name, l_rec.last_name
from t2
where id = l_id;
select email_address
into l_rec.email_address
from t2
where id = l_id;
select title
into l_title
from t2
where id = l_id;
select email_address
into l_rec.email_address
from t2
where id = l_id;
select title
into l_title
from t2
where id = l_id;
repeat for city, state, zip, country, phone, fax,
… … …
return l_rec;
End;
select *
into l_rec
from t2
where id = (select max(id2) from t1 where id = p_id);
• Why was that more than:
• And even then, why did it exist at all – they
probably call this function in a loop
– For x in (…)
• For y in (…query based on x…)
– For z in (…query based on x,y…)
Let’s tune a
Query!
The Schema Matters
• A Lot!
• Tune this query:
Select DOCUMENT_NAME, META_DATA
from documents
where userid=:x;
• That is about as easy as it gets (the SQL)
• Not too much we can do to rewrite it…
• But we’d like to make it better.
Iot01.sql
Cf.sql
Metadata
Matters
ops$tkyte%ORA10GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id (+)
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 2 |
| 1 | SORT AGGREGATE | | 1 | 65 | |
| 2 | NESTED LOOPS | | 1 | 65 | 2 |
| 3 | NESTED LOOPS | | 1 | 52 | 2 |
| 4 | TABLE ACCESS BY INDEX ROWID| T3 | 1 | 26 | 1 |
|* 5 | INDEX RANGE SCAN | T3_OLS_RS_1 | 1 | | 1 |
| 6 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 26 | 1 |
|* 7 | INDEX UNIQUE SCAN | T2_PK1 | 1 | | |
|* 8 | INDEX UNIQUE SCAN | T1_PK1 | 1 | 13 | |
------------------------------------------------------------------------------
The optimizer is getting smarter than we are…
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> CREATE TABLE T1
2 (
3 ORDER_ID NUMBER(18) NOT NULL,
4 ACCOUNT_NO NUMBER(10) NOT NULL,
5 ORDER_NUMBER VARCHAR2(20) NOT NULL,
6 data varchar2(1000)
7 );
Table created.
ops$tkyte%ORA11GR2> ALTER TABLE T1 ADD CONSTRAINT T1_PK1 PRIMARY KEY (ORDER_ID);
Table altered.
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> CREATE TABLE T2
2 (
3 SERVICE_ORDER_ID NUMBER(18) NOT NULL,
4 ORDER_ID NUMBER(18) NOT NULL,
5 ORDER_STATUS_ID NUMBER(6) NOT NULL,
6 data varchar2(1000)
7 );
Table created.
ops$tkyte%ORA11GR2> ALTER TABLE T2 ADD CONSTRAINT T2_PK1
2 PRIMARY KEY (SERVICE_ORDER_ID);
Table altered.
ops$tkyte%ORA11GR2> ALTER TABLE T2 ADD CONSTRAINT T2_OSO_FK1
2 FOREIGN KEY (ORDER_ID) REFERENCES T1 (ORDER_ID);
Table altered.
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> CREATE TABLE T3
2 (
3 SERVICE_ORDER_ID NUMBER(18) NOT NULL,
4 RELATED_SERVICE_ORDER_ID NUMBER(18),
5 data varchar2(1000)
6 );
Table created.
ops$tkyte%ORA11GR2> ALTER TABLE T3 ADD CONSTRAINT T3_ORDER_PK1
2 PRIMARY KEY (SERVICE_ORDER_ID);
Table altered.
ops$tkyte%ORA11GR2> ALTER TABLE T3 ADD CONSTRAINT T3_OLS_S_FK1
2 FOREIGN KEY (SERVICE_ORDER_ID) REFERENCES T2 (SERVICE_ORDER_ID);
Table altered.
ops$tkyte%ORA11GR2> CREATE INDEX T3_OLS_RS_1
2 ON T3 (RELATED_SERVICE_ORDER_ID);
Index created.
ops$tkyte%ORA10GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id (+)
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 65 | 2 |
| 1 | SORT AGGREGATE | | 1 | 65 | |
| 2 | NESTED LOOPS | | 1 | 65 | 2 |
| 3 | NESTED LOOPS | | 1 | 52 | 2 |
| 4 | TABLE ACCESS BY INDEX ROWID| T3 | 1 | 26 | 1 |
|* 5 | INDEX RANGE SCAN | T3_OLS_RS_1 | 1 | | 1 |
| 6 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 26 | 1 |
|* 7 | INDEX UNIQUE SCAN | T2_PK1 | 1 | | |
|* 8 | INDEX UNIQUE SCAN | T1_PK1 | 1 | 13 | |
------------------------------------------------------------------------------
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id (+)
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 26 | 1 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 26 | | |
|* 2 | INDEX RANGE SCAN| T3_OLS_RS_1 | 1 | 26 | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id (+)
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
• First, it knows the outer join is not necessary
– Where t2.col = t3.col(+) and t3.anything =
‘something’
– Implies the (+) is not necessary
• If the outer join ‘happened’, then t3.anything
would be NULL! And t3.anything =
to_number(:v0) would never be satisfied
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id (+)
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
• Second, it knows that T1 is not relevant to the query
– Nothing is selected from T1 in the output
– T1(order_id) is the primary key, joined to T2(order_id) – so T2
is “key preserved”
– T2(order_id) is NOT NULL and is a foreign key to T1
– Therefore, when you join T1 to T2 – every row in T2 appears
at least once and at most once in the output
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T2, T3
3 WHERE T2.service_order_id = T3.service_order_id
4 AND T3.related_service_order_id = TO_NUMBER(:v0);
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id (+)
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
• Lastly, it knows that T2 is not relevant to the query
– Nothing is selected from T2 in the output
– T2(service_order_id) is the primary key, joined to
T3(service_order_id) – so T3 is “key preserved”
– T3(service_order_id) is NOT NULL and is a foreign key to T2
– Therefore, when you join T2 to T3 – every row in T3 appears
at least once and at most once in the output
ops$tkyte%ORA11GR2> set autotrace traceonly explain
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T3
3 WHERE T3.related_service_order_id = TO_NUMBER(:v0);
The optimizer is getting smarter than we are…
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T1, T2, T3
3 WHERE T2.order_id = T1.order_id
4 AND T2.service_order_id = T3.service_order_id (+)
5 AND T3.related_service_order_id = TO_NUMBER(:v0);
ops$tkyte%ORA11GR2> SELECT COUNT(*)
2 FROM T3
3 WHERE T3.related_service_order_id = TO_NUMBER(:v0);
Is the same as…. But only
because of the constraints in
place…
Connection
Management
Demo
Youtube.com:
Holdsworth real world
How to tune a query - ODTUG 2012

More Related Content

What's hot

15. Streams Files and Directories
15. Streams Files and Directories 15. Streams Files and Directories
15. Streams Files and Directories Intro C# Book
 
Chapter 22. Lambda Expressions and LINQ
Chapter 22. Lambda Expressions and LINQChapter 22. Lambda Expressions and LINQ
Chapter 22. Lambda Expressions and LINQIntro C# Book
 
Data structure lab manual
Data structure lab manualData structure lab manual
Data structure lab manualnikshaikh786
 
Python lambda functions with filter, map & reduce function
Python lambda functions with filter, map & reduce functionPython lambda functions with filter, map & reduce function
Python lambda functions with filter, map & reduce functionARVIND PANDE
 
07. Java Array, Set and Maps
07.  Java Array, Set and Maps07.  Java Array, Set and Maps
07. Java Array, Set and MapsIntro C# Book
 
13 Strings and Text Processing
13 Strings and Text Processing13 Strings and Text Processing
13 Strings and Text ProcessingIntro C# Book
 
Compiler Construction | Lecture 14 | Interpreters
Compiler Construction | Lecture 14 | InterpretersCompiler Construction | Lecture 14 | Interpreters
Compiler Construction | Lecture 14 | InterpretersEelco Visser
 
Parts of python programming language
Parts of python programming languageParts of python programming language
Parts of python programming languageMegha V
 
Java Foundations: Arrays
Java Foundations: ArraysJava Foundations: Arrays
Java Foundations: ArraysSvetlin Nakov
 
02. Primitive Data Types and Variables
02. Primitive Data Types and Variables02. Primitive Data Types and Variables
02. Primitive Data Types and VariablesIntro C# Book
 
Library functions in c++
Library functions in c++Library functions in c++
Library functions in c++Neeru Mittal
 

What's hot (20)

Stack queue
Stack queueStack queue
Stack queue
 
Advanced C - Part 2
Advanced C - Part 2Advanced C - Part 2
Advanced C - Part 2
 
15. Streams Files and Directories
15. Streams Files and Directories 15. Streams Files and Directories
15. Streams Files and Directories
 
Chapter 22. Lambda Expressions and LINQ
Chapter 22. Lambda Expressions and LINQChapter 22. Lambda Expressions and LINQ
Chapter 22. Lambda Expressions and LINQ
 
10. Recursion
10. Recursion10. Recursion
10. Recursion
 
06.Loops
06.Loops06.Loops
06.Loops
 
Chapter 4
Chapter 4Chapter 4
Chapter 4
 
Data structure lab manual
Data structure lab manualData structure lab manual
Data structure lab manual
 
Python lambda functions with filter, map & reduce function
Python lambda functions with filter, map & reduce functionPython lambda functions with filter, map & reduce function
Python lambda functions with filter, map & reduce function
 
07. Java Array, Set and Maps
07.  Java Array, Set and Maps07.  Java Array, Set and Maps
07. Java Array, Set and Maps
 
13 Strings and Text Processing
13 Strings and Text Processing13 Strings and Text Processing
13 Strings and Text Processing
 
Compiler Construction | Lecture 14 | Interpreters
Compiler Construction | Lecture 14 | InterpretersCompiler Construction | Lecture 14 | Interpreters
Compiler Construction | Lecture 14 | Interpreters
 
Haskell 101
Haskell 101Haskell 101
Haskell 101
 
Parts of python programming language
Parts of python programming languageParts of python programming language
Parts of python programming language
 
Java Foundations: Arrays
Java Foundations: ArraysJava Foundations: Arrays
Java Foundations: Arrays
 
Stack, queue and hashing
Stack, queue and hashingStack, queue and hashing
Stack, queue and hashing
 
02. Primitive Data Types and Variables
02. Primitive Data Types and Variables02. Primitive Data Types and Variables
02. Primitive Data Types and Variables
 
Pointer
PointerPointer
Pointer
 
C Assignment Help
C Assignment HelpC Assignment Help
C Assignment Help
 
Library functions in c++
Library functions in c++Library functions in c++
Library functions in c++
 

Similar to How to tune a query - ODTUG 2012

Eff Plsql
Eff PlsqlEff Plsql
Eff Plsqlafa reg
 
Ch02 primitive-data-definite-loops
Ch02 primitive-data-definite-loopsCh02 primitive-data-definite-loops
Ch02 primitive-data-definite-loopsJames Brotsos
 
Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4Abed Bukhari
 
Getting started cpp full
Getting started cpp   fullGetting started cpp   full
Getting started cpp fullVõ Hòa
 
Code Tuning
Code TuningCode Tuning
Code Tuningbgtraghu
 
Java Foundations: Data Types and Type Conversion
Java Foundations: Data Types and Type ConversionJava Foundations: Data Types and Type Conversion
Java Foundations: Data Types and Type ConversionSvetlin Nakov
 
Advance data structure & algorithm
Advance data structure & algorithmAdvance data structure & algorithm
Advance data structure & algorithmK Hari Shankar
 
An Overview of SystemVerilog for Design and Verification
An Overview of SystemVerilog  for Design and VerificationAn Overview of SystemVerilog  for Design and Verification
An Overview of SystemVerilog for Design and VerificationKapilRaghunandanTrip
 
The purpose of this C++ programming project is to allow the student .pdf
The purpose of this C++ programming project is to allow the student .pdfThe purpose of this C++ programming project is to allow the student .pdf
The purpose of this C++ programming project is to allow the student .pdfRahul04August
 
C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0Yaser Zhian
 
MariaDB Server 10.3 - Temporale Daten und neues zur DB-Kompatibilität
MariaDB Server 10.3 - Temporale Daten und neues zur DB-KompatibilitätMariaDB Server 10.3 - Temporale Daten und neues zur DB-Kompatibilität
MariaDB Server 10.3 - Temporale Daten und neues zur DB-KompatibilitätMariaDB plc
 

Similar to How to tune a query - ODTUG 2012 (20)

Eff Plsql
Eff PlsqlEff Plsql
Eff Plsql
 
Chapter 2
Chapter 2Chapter 2
Chapter 2
 
Ch02 primitive-data-definite-loops
Ch02 primitive-data-definite-loopsCh02 primitive-data-definite-loops
Ch02 primitive-data-definite-loops
 
CP 04.pptx
CP 04.pptxCP 04.pptx
CP 04.pptx
 
Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4
 
Getting started cpp full
Getting started cpp   fullGetting started cpp   full
Getting started cpp full
 
PL/SQL and radix tree structure
PL/SQL and radix tree structurePL/SQL and radix tree structure
PL/SQL and radix tree structure
 
Code Tuning
Code TuningCode Tuning
Code Tuning
 
Java Foundations: Data Types and Type Conversion
Java Foundations: Data Types and Type ConversionJava Foundations: Data Types and Type Conversion
Java Foundations: Data Types and Type Conversion
 
Advance data structure & algorithm
Advance data structure & algorithmAdvance data structure & algorithm
Advance data structure & algorithm
 
An Overview of SystemVerilog for Design and Verification
An Overview of SystemVerilog  for Design and VerificationAn Overview of SystemVerilog  for Design and Verification
An Overview of SystemVerilog for Design and Verification
 
Stored procedure
Stored procedureStored procedure
Stored procedure
 
PSI 3 Integration
PSI 3 IntegrationPSI 3 Integration
PSI 3 Integration
 
Lec2
Lec2Lec2
Lec2
 
Lec2&3 data structure
Lec2&3 data structureLec2&3 data structure
Lec2&3 data structure
 
The purpose of this C++ programming project is to allow the student .pdf
The purpose of this C++ programming project is to allow the student .pdfThe purpose of this C++ programming project is to allow the student .pdf
The purpose of this C++ programming project is to allow the student .pdf
 
lecture12.ppt
lecture12.pptlecture12.ppt
lecture12.ppt
 
C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0C++11 - A Change in Style - v2.0
C++11 - A Change in Style - v2.0
 
PHP tips by a MYSQL DBA
PHP tips by a MYSQL DBAPHP tips by a MYSQL DBA
PHP tips by a MYSQL DBA
 
MariaDB Server 10.3 - Temporale Daten und neues zur DB-Kompatibilität
MariaDB Server 10.3 - Temporale Daten und neues zur DB-KompatibilitätMariaDB Server 10.3 - Temporale Daten und neues zur DB-Kompatibilität
MariaDB Server 10.3 - Temporale Daten und neues zur DB-Kompatibilität
 

More from Connor McDonald

Sangam 19 - PLSQL still the coolest
Sangam 19 - PLSQL still the coolestSangam 19 - PLSQL still the coolest
Sangam 19 - PLSQL still the coolestConnor McDonald
 
Sangam 19 - Analytic SQL
Sangam 19 - Analytic SQLSangam 19 - Analytic SQL
Sangam 19 - Analytic SQLConnor McDonald
 
UKOUG - 25 years of hints and tips
UKOUG - 25 years of hints and tipsUKOUG - 25 years of hints and tips
UKOUG - 25 years of hints and tipsConnor McDonald
 
Sangam 19 - Successful Applications on Autonomous
Sangam 19 - Successful Applications on AutonomousSangam 19 - Successful Applications on Autonomous
Sangam 19 - Successful Applications on AutonomousConnor McDonald
 
Sangam 2019 - The Latest Features
Sangam 2019 - The Latest FeaturesSangam 2019 - The Latest Features
Sangam 2019 - The Latest FeaturesConnor McDonald
 
UKOUG 2019 - SQL features
UKOUG 2019 - SQL featuresUKOUG 2019 - SQL features
UKOUG 2019 - SQL featuresConnor McDonald
 
APEX tour 2019 - successful development with autonomous
APEX tour 2019 - successful development with autonomousAPEX tour 2019 - successful development with autonomous
APEX tour 2019 - successful development with autonomousConnor McDonald
 
APAC Groundbreakers 2019 - Perth/Melbourne
APAC Groundbreakers 2019 - Perth/Melbourne APAC Groundbreakers 2019 - Perth/Melbourne
APAC Groundbreakers 2019 - Perth/Melbourne Connor McDonald
 
OOW19 - Flashback, not just for DBAs
OOW19 - Flashback, not just for DBAsOOW19 - Flashback, not just for DBAs
OOW19 - Flashback, not just for DBAsConnor McDonald
 
OOW19 - Read consistency
OOW19 - Read consistencyOOW19 - Read consistency
OOW19 - Read consistencyConnor McDonald
 
OOW19 - Slower and less secure applications
OOW19 - Slower and less secure applicationsOOW19 - Slower and less secure applications
OOW19 - Slower and less secure applicationsConnor McDonald
 
OOW19 - Killing database sessions
OOW19 - Killing database sessionsOOW19 - Killing database sessions
OOW19 - Killing database sessionsConnor McDonald
 
OOW19 - Ten Amazing SQL features
OOW19 - Ten Amazing SQL featuresOOW19 - Ten Amazing SQL features
OOW19 - Ten Amazing SQL featuresConnor McDonald
 
Latin America Tour 2019 - 18c and 19c featues
Latin America Tour 2019   - 18c and 19c featuesLatin America Tour 2019   - 18c and 19c featues
Latin America Tour 2019 - 18c and 19c featuesConnor McDonald
 
Latin America tour 2019 - Flashback
Latin America tour 2019 -  FlashbackLatin America tour 2019 -  Flashback
Latin America tour 2019 - FlashbackConnor McDonald
 
Latin America Tour 2019 - 10 great sql features
Latin America Tour 2019  - 10 great sql featuresLatin America Tour 2019  - 10 great sql features
Latin America Tour 2019 - 10 great sql featuresConnor McDonald
 
Latin America Tour 2019 - pattern matching
Latin America Tour 2019 - pattern matchingLatin America Tour 2019 - pattern matching
Latin America Tour 2019 - pattern matchingConnor McDonald
 
Latin America Tour 2019 - slow data and sql processing
Latin America Tour 2019  - slow data and sql processingLatin America Tour 2019  - slow data and sql processing
Latin America Tour 2019 - slow data and sql processingConnor McDonald
 

More from Connor McDonald (20)

Flashback ITOUG
Flashback ITOUGFlashback ITOUG
Flashback ITOUG
 
Sangam 19 - PLSQL still the coolest
Sangam 19 - PLSQL still the coolestSangam 19 - PLSQL still the coolest
Sangam 19 - PLSQL still the coolest
 
Sangam 19 - Analytic SQL
Sangam 19 - Analytic SQLSangam 19 - Analytic SQL
Sangam 19 - Analytic SQL
 
UKOUG - 25 years of hints and tips
UKOUG - 25 years of hints and tipsUKOUG - 25 years of hints and tips
UKOUG - 25 years of hints and tips
 
Sangam 19 - Successful Applications on Autonomous
Sangam 19 - Successful Applications on AutonomousSangam 19 - Successful Applications on Autonomous
Sangam 19 - Successful Applications on Autonomous
 
Sangam 2019 - The Latest Features
Sangam 2019 - The Latest FeaturesSangam 2019 - The Latest Features
Sangam 2019 - The Latest Features
 
UKOUG 2019 - SQL features
UKOUG 2019 - SQL featuresUKOUG 2019 - SQL features
UKOUG 2019 - SQL features
 
APEX tour 2019 - successful development with autonomous
APEX tour 2019 - successful development with autonomousAPEX tour 2019 - successful development with autonomous
APEX tour 2019 - successful development with autonomous
 
APAC Groundbreakers 2019 - Perth/Melbourne
APAC Groundbreakers 2019 - Perth/Melbourne APAC Groundbreakers 2019 - Perth/Melbourne
APAC Groundbreakers 2019 - Perth/Melbourne
 
OOW19 - Flashback, not just for DBAs
OOW19 - Flashback, not just for DBAsOOW19 - Flashback, not just for DBAs
OOW19 - Flashback, not just for DBAs
 
OOW19 - Read consistency
OOW19 - Read consistencyOOW19 - Read consistency
OOW19 - Read consistency
 
OOW19 - Slower and less secure applications
OOW19 - Slower and less secure applicationsOOW19 - Slower and less secure applications
OOW19 - Slower and less secure applications
 
OOW19 - Killing database sessions
OOW19 - Killing database sessionsOOW19 - Killing database sessions
OOW19 - Killing database sessions
 
OOW19 - Ten Amazing SQL features
OOW19 - Ten Amazing SQL featuresOOW19 - Ten Amazing SQL features
OOW19 - Ten Amazing SQL features
 
Latin America Tour 2019 - 18c and 19c featues
Latin America Tour 2019   - 18c and 19c featuesLatin America Tour 2019   - 18c and 19c featues
Latin America Tour 2019 - 18c and 19c featues
 
Latin America tour 2019 - Flashback
Latin America tour 2019 -  FlashbackLatin America tour 2019 -  Flashback
Latin America tour 2019 - Flashback
 
Latin America Tour 2019 - 10 great sql features
Latin America Tour 2019  - 10 great sql featuresLatin America Tour 2019  - 10 great sql features
Latin America Tour 2019 - 10 great sql features
 
Latin America Tour 2019 - pattern matching
Latin America Tour 2019 - pattern matchingLatin America Tour 2019 - pattern matching
Latin America Tour 2019 - pattern matching
 
Latin America Tour 2019 - slow data and sql processing
Latin America Tour 2019  - slow data and sql processingLatin America Tour 2019  - slow data and sql processing
Latin America Tour 2019 - slow data and sql processing
 
ANSI vs Oracle language
ANSI vs Oracle languageANSI vs Oracle language
ANSI vs Oracle language
 

Recently uploaded

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Neo4j
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
 

Recently uploaded (20)

Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort ServiceHot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
 

How to tune a query - ODTUG 2012

  • 1. How to “tune” a query Thomas Kyte http://asktom.oracle.com/
  • 2. Agenda • What single thing has the most impact on performance and scalability? • Doing It Wrong • Doing It Still Wrong • Tuning A Query • Adding Information • Connection Management
  • 6. Some Basic Math • You write a routine that executes in 1ms (1/1000th of a second) • You have 5,000,000 things to process • That is 5,000 seconds! • That is 83 minutes! • That is almost 1.5 hours!!! • Oh, but we’ll do parallel threads in our application…
  • 7. Would you like to load and validate one row 5,000,000 times or load and validate 5,000,000 rows once?
  • 8. create or replace procedure slow_by_slow as begin for x in (select rowid rid, object_name from t t_slow_by_slow) loop x.object_name := substr(x.object_name,2) ||substr(x.object_name,1,1); update t set object_name = x.object_name where rowid = x.rid; end loop; end; /
  • 9. create or replace procedure bulk as type ridArray is table of rowid; type onameArray is table of t.object_name%type; cursor c is select rowid rid, object_name from t t_bulk; l_rids ridArray; l_onames onameArray; N number := 100; begin open c; loop fetch c bulk collect into l_rids, l_onames limit N; for i in 1 .. l_rids.count loop l_onames(i) := substr(l_onames(i),2) ||substr(l_onames(i),1,1); end loop; forall i in 1 .. l_rids.count update t set object_name = l_onames(i) where rowid = l_rids(i); exit when c%notfound; end loop; close c; end;
  • 10. update t set object_name = substr(object_name,2) || substr(object_name,1,1);
  • 11. create table new_table as select OWNER, substr(object_name,2) || substr(object_name,1,1) OBJECT_NAME, SUBOBJECT_NAME, OBJECT_ID, DATA_OBJECT_ID, OBJECT_TYPE, CREATED, LAST_DDL_TIME, TIMESTAMP, STATUS, TEMPORARY, GENERATED, SECONDARY, NAMESPACE, EDITION_NAME from t;
  • 12. Method CPU units of time Slow by Slow 495 Bulk 193 (39%) Single SQL statement 91 (18%) *near order of magnitude CTAS 28 (5%) *two orders of magnitude
  • 16. Code… • Write as much code: – As you have to – But as little as you can… • Think in SETS • Use (really use – not just ‘use’) SQL Begin For x in ( select * from table@remote_db ) Loop Insert into table ( c1, c2, … ) values ( x.c1, x.c2,… ); End loop; End; Insert into table (c1,c2,…) select c1,c2,…. From table@remote_db Insert into table (c1,c2,…) select c1,c2,…. From table@remote_db LOG ERRORS ( some_variable ) REJECT LIMIT UNLIMITED; … code to handle errors for tag some_variable …
  • 17. More Code = More Bugs Less Code = Less Bugs • Always look at the procedural code and ask yourself “is there a set based way to do this algorithm” – For example …
  • 18. insert into t ( .... ) select EMPNO, STATUS_DATE, .... from t1, t2, t3, t4, .... where ....; loop delete from t where (EMPNO,STATUS_DATE) in ( select EMPNO, min(STATUS_DATE) from t group by EMPNO having count(1) > 1 ); exit when sql%rowcount = 0; end loop; More Code = More Bugs Less Code = Less Bugs
  • 19. insert into t ( .... ) select EMPNO, STATUS_DATE, .... from t1, t2, t3, t4, .... where ....; loop delete from t where (EMPNO,STATUS_DATE) in ( select EMPNO, min(STATUS_DATE) from t group by EMPNO having count(1) > 1 ); exit when sql%rowcount = 0; end loop; For any set of records with more than one EMPNO, remove rows with the oldest STATUS_DATE. Additionally – If the last set of EMPNO records all have the same STATUS_DATE, remove them all. More Code = More Bugs Less Code = Less Bugs EMPNO STATUS_DATE ------- ------------ 1 01-jan-2009 1 15-jun-2009 1 01-sep-2009 … 1000 01-feb-2009 1000 22-aug-2009 1000 10-oct-2009 1000 10-oct-2009(1,01-jan-2009,3) (1001,01-feb-2009,4) EMPNO STATUS_DATE ------- ------------ 1 01-jan-2009 1 15-jun-2009 1 01-sep-2009 … 1000 01-feb-2009 1000 22-aug-2009 1000 10-oct-2009 1000 10-oct-2009 EMPNO STATUS_DATE ------- ------------ 1 01-jan-2009 1 15-jun-2009 1 01-sep-2009 … 1000 01-feb-2009 1000 22-aug-2009 1000 10-oct-2009 1000 10-oct-2009(1,15-jun-2009,2) (1001,22-aug-2009,3) EMPNO STATUS_DATE ------- ------------ 1 01-jan-2009 1 15-jun-2009 1 01-sep-2009 … 1000 01-feb-2009 1000 22-aug-2009 1000 10-oct-2009 1000 10-oct-2009(1001,22-aug-2009,2) EMPNO STATUS_DATE ------- ------------ 1 01-sep-2009 …
  • 20. insert /* APPEND */ into t ( .... ) select EMPNO, STATUS_DATE, ...... from ( select EMPNO, STATUS_DATE, .... , max(STATUS_DATE) OVER ( partition by EMPNO ) max_sd, count(EMPNO) OVER ( partition by EMPNO,STATUS_DATE ) cnt from t1, t2, t3, t4, … where … ) where STATUS_DATE = max_sd and cnt = 1; More Code = More Bugs Less Code = Less Bugs • This was a data warehouse load (load 2-3-4 times the data you want, then delete? Ouch) • It was wrong – procedural code is no easier to understand than set based code, documentation is key
  • 21. /* Load table t using history tables. History tables have multiple records per employee. We need to keep the history records for each employee that have the maximum status date for that employee. We do that by computing the max(status_date) for each employee (partition by EMPNO finding max(status_date) and keeping only the records such that the status_date for that record = max(status_date) for all records with same empno */ insert /* APPEND */ into t ( .... ) select EMPNO, STATUS_DATE, ...... from ( select EMPNO, STATUS_DATE, .... , max(STATUS_DATE) OVER ( partition by EMPNO ) max_sd from t1, t2, t3, t4, … where … ) where STATUS_DATE = max_sd; More Code = More Bugs Less Code = Less Bugs
  • 22. /* Load table t using history tables. History tables have multiple records per employee. We need to keep the history records for each employee that have the maximum status date for that employee. We do that by numbering each history record by empno, keeping only the records such that it is the first record for a EMPNO after sorting by status_date desc. REALIZE: this is not deterministic if there are two status_dates that are the same for a given employee! */ insert /* APPEND */ into t ( .... ) select EMPNO, STATUS_DATE, ...... from ( select EMPNO, STATUS_DATE, .... , row_number() OVER ( partition by EMPNO order by STATUS_DATE desc ) rn from t1, t2, t3, t4, … where … ) where rn=1; More Code = More Bugs Less Code = Less Bugs
  • 24. Use PL/SQL constructs only when SQL cannot do it • Another coding ‘technique’ I see frequently: • The developer did not want to “burden” the database with a join For a in ( select * from t1 ) Loop For b in ( select * from t2 where t2.key = a.key ) Loop For c in ( select * from t3 where t3.key = b.key ) Loop …
  • 25. Create or replace function get_xxxx( … ) return … • Be fearful of functions that start with GET_ • You cannot tune them, they are already tuned – They use the simplest of SQL
  • 26. Create or replace function get_emp_details ( p_id in number ) return emp_record As l_rec emp_record; l_id number; Begin select max(id2) into l_id from t1 where id = p_id;
  • 27. Create or replace function get_emp_details ( p_id in number ) return emp_record As l_rec emp_record; l_id number; Begin select max(id2) into l_id from t1 where id = p_id; select first_name, last_name into l_rec.first_name, l_rec.last_name from t2 where id = l_id;
  • 28. select first_name, last_name into l_rec.first_name, l_rec.last_name from t2 where id = l_id; select email_address into l_rec.email_address from t2 where id = l_id; select title into l_title from t2 where id = l_id;
  • 29. select email_address into l_rec.email_address from t2 where id = l_id; select title into l_title from t2 where id = l_id; repeat for city, state, zip, country, phone, fax, … … … return l_rec; End;
  • 30. select * into l_rec from t2 where id = (select max(id2) from t1 where id = p_id); • Why was that more than: • And even then, why did it exist at all – they probably call this function in a loop – For x in (…) • For y in (…query based on x…) – For z in (…query based on x,y…)
  • 32. The Schema Matters • A Lot! • Tune this query: Select DOCUMENT_NAME, META_DATA from documents where userid=:x; • That is about as easy as it gets (the SQL) • Not too much we can do to rewrite it… • But we’d like to make it better. Iot01.sql Cf.sql
  • 34. ops$tkyte%ORA10GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 65 | 2 | | 1 | SORT AGGREGATE | | 1 | 65 | | | 2 | NESTED LOOPS | | 1 | 65 | 2 | | 3 | NESTED LOOPS | | 1 | 52 | 2 | | 4 | TABLE ACCESS BY INDEX ROWID| T3 | 1 | 26 | 1 | |* 5 | INDEX RANGE SCAN | T3_OLS_RS_1 | 1 | | 1 | | 6 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 26 | 1 | |* 7 | INDEX UNIQUE SCAN | T2_PK1 | 1 | | | |* 8 | INDEX UNIQUE SCAN | T1_PK1 | 1 | 13 | | ------------------------------------------------------------------------------ The optimizer is getting smarter than we are…
  • 35. The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> CREATE TABLE T1 2 ( 3 ORDER_ID NUMBER(18) NOT NULL, 4 ACCOUNT_NO NUMBER(10) NOT NULL, 5 ORDER_NUMBER VARCHAR2(20) NOT NULL, 6 data varchar2(1000) 7 ); Table created. ops$tkyte%ORA11GR2> ALTER TABLE T1 ADD CONSTRAINT T1_PK1 PRIMARY KEY (ORDER_ID); Table altered.
  • 36. The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> CREATE TABLE T2 2 ( 3 SERVICE_ORDER_ID NUMBER(18) NOT NULL, 4 ORDER_ID NUMBER(18) NOT NULL, 5 ORDER_STATUS_ID NUMBER(6) NOT NULL, 6 data varchar2(1000) 7 ); Table created. ops$tkyte%ORA11GR2> ALTER TABLE T2 ADD CONSTRAINT T2_PK1 2 PRIMARY KEY (SERVICE_ORDER_ID); Table altered. ops$tkyte%ORA11GR2> ALTER TABLE T2 ADD CONSTRAINT T2_OSO_FK1 2 FOREIGN KEY (ORDER_ID) REFERENCES T1 (ORDER_ID); Table altered.
  • 37. The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> CREATE TABLE T3 2 ( 3 SERVICE_ORDER_ID NUMBER(18) NOT NULL, 4 RELATED_SERVICE_ORDER_ID NUMBER(18), 5 data varchar2(1000) 6 ); Table created. ops$tkyte%ORA11GR2> ALTER TABLE T3 ADD CONSTRAINT T3_ORDER_PK1 2 PRIMARY KEY (SERVICE_ORDER_ID); Table altered. ops$tkyte%ORA11GR2> ALTER TABLE T3 ADD CONSTRAINT T3_OLS_S_FK1 2 FOREIGN KEY (SERVICE_ORDER_ID) REFERENCES T2 (SERVICE_ORDER_ID); Table altered. ops$tkyte%ORA11GR2> CREATE INDEX T3_OLS_RS_1 2 ON T3 (RELATED_SERVICE_ORDER_ID); Index created.
  • 38. ops$tkyte%ORA10GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); ------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost | ------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 1 | 65 | 2 | | 1 | SORT AGGREGATE | | 1 | 65 | | | 2 | NESTED LOOPS | | 1 | 65 | 2 | | 3 | NESTED LOOPS | | 1 | 52 | 2 | | 4 | TABLE ACCESS BY INDEX ROWID| T3 | 1 | 26 | 1 | |* 5 | INDEX RANGE SCAN | T3_OLS_RS_1 | 1 | | 1 | | 6 | TABLE ACCESS BY INDEX ROWID| T2 | 1 | 26 | 1 | |* 7 | INDEX UNIQUE SCAN | T2_PK1 | 1 | | | |* 8 | INDEX UNIQUE SCAN | T1_PK1 | 1 | 13 | | ------------------------------------------------------------------------------ The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); --------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 26 | 1 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 26 | | | |* 2 | INDEX RANGE SCAN| T3_OLS_RS_1 | 1 | 26 | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------
  • 39. The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); • First, it knows the outer join is not necessary – Where t2.col = t3.col(+) and t3.anything = ‘something’ – Implies the (+) is not necessary • If the outer join ‘happened’, then t3.anything would be NULL! And t3.anything = to_number(:v0) would never be satisfied ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id 5 AND T3.related_service_order_id = TO_NUMBER(:v0);
  • 40. The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); • Second, it knows that T1 is not relevant to the query – Nothing is selected from T1 in the output – T1(order_id) is the primary key, joined to T2(order_id) – so T2 is “key preserved” – T2(order_id) is NOT NULL and is a foreign key to T1 – Therefore, when you join T1 to T2 – every row in T2 appears at least once and at most once in the output ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T2, T3 3 WHERE T2.service_order_id = T3.service_order_id 4 AND T3.related_service_order_id = TO_NUMBER(:v0);
  • 41. The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); • Lastly, it knows that T2 is not relevant to the query – Nothing is selected from T2 in the output – T2(service_order_id) is the primary key, joined to T3(service_order_id) – so T3 is “key preserved” – T3(service_order_id) is NOT NULL and is a foreign key to T2 – Therefore, when you join T2 to T3 – every row in T3 appears at least once and at most once in the output ops$tkyte%ORA11GR2> set autotrace traceonly explain ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T3 3 WHERE T3.related_service_order_id = TO_NUMBER(:v0);
  • 42. The optimizer is getting smarter than we are… ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T1, T2, T3 3 WHERE T2.order_id = T1.order_id 4 AND T2.service_order_id = T3.service_order_id (+) 5 AND T3.related_service_order_id = TO_NUMBER(:v0); ops$tkyte%ORA11GR2> SELECT COUNT(*) 2 FROM T3 3 WHERE T3.related_service_order_id = TO_NUMBER(:v0); Is the same as…. But only because of the constraints in place…