Whether you are a developer or DBA, this presentation will outline a method for determining the best approach for tuning a query every time by utilizing response time analysis and SQL Diagramming techniques. Regardless of the complexity of the statement or database platform being utilized (this method works on all), this quick and systematic approach will lead you down the correct
tuning path with no guessing. If you are a beginner or expert, this approach will save you countless hours tuning a query.
Oracle Query Tuning Tips - Get it Right the First Time
1. SQL Query Tuning Tips for Oracle
Get it Right the First Time
Dean Richards
Lead DBA, Confio Software
2/21/2013 1
2. 2/21/2013 Confidential - Internal Use Only 2
Who Am I?
• 20+ Years in Oracle & SQL Server
– DBA and Developer
– Worked for Oracle SPG
• Lead DBA for Confio Software
– DeanRichards@confio.com
– Makers of Ignite8 Response Time Analysis Tools
– http://www.ignitefree.com – only free RTA Tool
• Specialize in Performance Tuning
3. 2/21/2013 3
Agenda
• Introduction
• Which Query Should I Tune?
• Query Plans
• SQL Diagramming
– Who registered yesterday for Tuning Class
– Check order status
4. 2/21/2013 Confidential - Internal Use Only 4
Why Focus on Queries
• Most Applications
– Read and Write data to/from database
– Simple manipulation of data
– Deal with smaller amounts of data
• Most Databases
– Examine larger amounts of data, return a little
– Inefficiencies quickly become bottleneck
• Why do SQL tuning?
– Tuning SQL - “Gives the most bang for your buck”
– Changes to SQL are Safer
– ~85% of performance issues are SQL related
5. 2/21/2013 Confidential - Internal Use Only 5
Who Should Tune
• Developers?
– Developing applications is very difficult
– Typically focused on functionality
– Not much time left to tune SQL
– Do not get enough practice
– SQL runs differently in Production than Dev/Test
• DBA?
– Do not know the code like developers do
– Focus on “Keep the Lights On”
– Very complex environment
• Need a team approach
– But also need someone to lead the effort
6. 2/21/2013 Confidential - Internal Use Only 6
Which SQL
• User / Batch Job Complaints
• Queries Performing Most I/O (LIO, PIO)
• Queries Consuming CPU
• Queries Doing Full Table Scans
• Known Poorly Performing SQL
• Tracing a Session / Process
• Highest Response Times (Ignite for Oracle)
select sql_text, elapsed_time, cpu_time, executions,
sql_id, hash_value, last_active_time,
plan_hash_value, program_id, module, action
from v$sql
order by elapsed_time
/
7. 2/21/2013 Confidential - Internal Use Only 7
SQL Wait States
Understand the total time a Query spends in Database
Measure time while Query executes
SQL Server helps by providing Wait Types
Focus on Response Time
8. 2/21/2013 Confidential - Internal Use Only 8
Oracle Wait Interface
V$SQL
SQL_ID
SQL_FULLTEXT
V$SESSION
SID
USERNAME
SQL_ID
PROGRAM
MODULE
ACTION
PLAN_HASH_VALUE
ROW_WAIT_OBJ#
V$SQLAREA
SQL_ID
EXECUTIONS
PARSE_CALLS
BUFFER_GETS
DISK_READS
V$SESSION_WAIT
SID
EVENT
P1, P1RAW, P2, P2RAW, P3, P3RAW
STATE (WAITING, WAITED…)
• Oracle 10g added this info to V$SESSION
V$SQL_PLAN
SQL_ID
PLAN_HASH_VALUE
DBA_OBJECTS
OBJECT_ID
OBJECT_NAME
OBJECT_TYPE
9. 2/21/2013 Confidential - Internal Use Only 9
Oracle Wait Interface
SELECT s.sql_id, sql.sql_text, sql.plan_hash_value,
DECODE(s.state, 'WAITING', s.event, 'CPU/LogicalIO') waitevent,
s.p1, s.p2, s.p3
FROM v$session s, v$sql sql
WHERE s.sql_id = sql.sql_id
AND s.sql_address = sql.address
AND program = ‘…’ AND s.sid = …
AND <whatever else you know>
10. 2/21/2013 Confidential - Internal Use Only 10
SQL Plan
• EXPLAIN PLAN
– Estimated execution plan
– Can be wrong for many reasons
– What most tools show you
• V$SQL_PLAN (Oracle 9i+)
– Real execution plan
– Use DBMS_XPLAN for display
• Confio Ignite for Oracle
– Gathers real plan
– Shows it historically
11. 2/21/2013 Confidential - Internal Use Only 11
DBMS_XPLAN
select * from table (
dbms_xplan.display_cursor(‘&SQLID’,ChildNo));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID f7jqysdfu2f2s, child number 0
-------------------------------------
update emp set hire_date = to_date(':1') where id=:2
Plan hash value: 1494045816
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | UPDATE STATEMENT | | | | 3025 (100)| |
| 1 | UPDATE | EMP | | | | |
|* 2 | TABLE ACCESS FULL| EMP | 122 | 2684 | 3025 (1)| 00:00:37 |
---------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("ID"=3247)
12. 2/21/2013 Confidential - Internal Use Only 12
All Plans Not Equal
SELECT company, attribute FROM data_out WHERE segment = :B1
• Wait Time – 100% on “db file scattered read”
• Plan from EXPLAIN PLAN
• Plan from V$SQL_PLAN using DBMS_XPLAN
13. 2/21/2013 Confidential - Internal Use Only 13
Bind Values
• V$SQL_BIND_CAPTURE
– STATISTICS_LEVEL = TYPICAL or ALL
– Collected at 15 minute intervals
SELECT name, position, datatype_string, value_string
FROM v$sql_bind_capture
WHERE sql_id = '15uughacxfh13';
NAME POSITION DATATYPE_STRING VALUE_STRING
----- ---------- --------------- ------------
:B1 1 BINARY_DOUBLE 189
• Bind Values also provided by tracing
– Level 4 – bind values
– Level 8 – wait information
– Level 12 – bind values and wait information
14. 2/21/2013 Confidential - Internal Use Only 14
Case Studies
• SQL Diagramming
• Case Study 1
– Who registered for Tuning Class yesterday
• Case Study 2
– Check order status
15. 2/21/2013 Confidential - Internal Use Only 15
Case Study 1
• Who registered yesterday for SQL Tuning
SELECT s.fname, s.lname, r.signup_date
FROM student s
INNER JOIN registration r ON s.student_id = r.student_id
INNER JOIN class c ON r.class_id = c.class_id
WHERE c.name = 'SQL TUNING'
AND r.signup_date BETWEEN :1 AND :2
AND r.cancelled = 'N'
• Execution Stats – 12,634 Logical Reads
18. 2/21/2013 Confidential - Internal Use Only 18
SQL Diagramming
registration
student class
5
1
30
1
.03
.001
select count(1) from registration where cancelled = 'N'
and signup_date between '2012-12-05 00:00' and '2012-12-06 00:00'
54,554 / 1,639,186 = 0.03
select count(1) from class where name = 'SQL TUNING'
2 / 1,267 = .001
• Great Book - “SQL Tuning” by Dan Tow
– Teaches SQL Diagramming
– http://www.singingsql.com
19. 2/21/2013 Confidential - Internal Use Only 19
New Execution Plan
• Execution Stats – 11,168 Logical Reads
• Why does a Full Table Scan still occur on REGISTRATION?
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 819 | 3153 (5)| 00:00:38 |
|* 1 | FILTER | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 7 | 819 | 3153 (5)| 00:00:38 |
|* 4 | HASH JOIN | | 7 | 448 | 3146 (5)| 00:00:38 |
| 5 | TABLE ACCESS BY INDEX ROWID| CLASS | 1 | 45 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | CL_NAME | 1 | | 1 (0)| 00:00:01 |
|* 7 | TABLE ACCESS FULL | REGISTRATION | 8334 | 154K| 3144 (5)| 00:00:38 |
|* 8 | INDEX UNIQUE SCAN | PK_STUDENT | 1 | | 0 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID | STUDENT | 1 | 53 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
CREATE INDEX cl_name ON class(name)
21. 2/21/2013 Confidential - Internal Use Only 21
New Execution Plan
CREATE INDEX reg_alt ON registration(class_id)
• Execution Stats – 4,831 Logical Reads
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 7 | 819 | 2415 (1)| 00:00:29 |
|* 1 | FILTER | | | | | |
| 2 | NESTED LOOPS | | | | | |
| 3 | NESTED LOOPS | | 7 | 819 | 2415 (1)| 00:00:29 |
| 4 | NESTED LOOPS | | 7 | 448 | 2408 (1)| 00:00:29 |
| 5 | TABLE ACCESS BY INDEX ROWID| CLASS | 1 | 45 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | CL_NAME | 1 | | 1 (0)| 00:00:01 |
|* 7 | TABLE ACCESS BY INDEX ROWID| REGISTRATION | 7 | 133 | 2406 (1)| 00:00:29 |
|* 8 | INDEX RANGE SCAN | REG_ALT | 2667 | | 9 (0)| 00:00:01 |
|* 9 | INDEX UNIQUE SCAN | PK_STUDENT | 1 | | 0 (0)| 00:00:01 |
| 10 | TABLE ACCESS BY INDEX ROWID | STUDENT | 1 | 53 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------
22. 2/21/2013 Confidential - Internal Use Only 22
Case Study 2
• Lookup order status for caller
SELECT o.OrderID, c.LastName, p.ProductID, p.Description,
sd.ActualShipDate, sd.ShipStatus, sd.ExpectedShipDate
FROM Orders o
INNER JOIN Item i ON i.OrderID = o.OrderID
INNER JOIN Customer c ON c.CustomerID = o.CustomerID
INNER JOIN ShipmentDetails sd ON sd.ShipmentID = i.ShipmentID
INNER JOIN Product p ON p.ProductID = i.ProductID
INNER JOIN Address a ON a.AddressID = sd.AddressID
WHERE c.LastName LIKE NVL(:1,'') || '%'
AND c.FirstName LIKE NVL(:2,'') || '%'
AND o.OrderDate >= SYSDATE - 30
AND o.OrderStatus <> 'C'
• Execution Stats – 73,600 Logical Reads
23. 2/21/2013 Confidential - Internal Use Only 23
Case Study 2
• Lookup order status for caller
SELECT o.OrderID, c.LastName, p.ProductID, p.Description,
sd.ActualShipDate, sd.ShipStatus, sd.ExpectedShipDate
FROM Orders o
INNER JOIN Item i ON i.OrderID = o.OrderID
INNER JOIN Customer c ON c.CustomerID = o.CustomerID
INNER JOIN ShipmentDetails sd ON sd.ShipmentID = i.ShipmentID
INNER JOIN Product p ON p.ProductID = i.ProductID
INNER JOIN Address a ON a.AddressID = sd.AddressID
WHERE c.LastName LIKE 'SMI%'
AND c.FirstName LIKE '%'
AND o.OrderDate >= SYSDATE - 30
AND o.OrderStatus <> 'C'
• Execution Stats – 73,600 Logical Reads
26. 2/21/2013 Confidential - Internal Use Only 26
SELECT COUNT(1) FROM Customer
WHERE LastName LIKE 'SMI%'
1917 / 52189 = .04
SELECT COUNT(1) FROM Orders
WHERE OrderDate >= SYSDATE - 30
55067 / 690671 = .08
SELECT COUNT(1) FROM Orders
WHERE OrderStatus <> 'C'
3760 / 690671 = .005
o .08
.04i c
psd
a
.005
SQL Diagram
27. 2/21/2013 Confidential - Internal Use Only 27
• Only 0.5% of rows are <> ‘C’
• How about changing the query?
– AND o.OrderStatus = 'I'
• Add an Index on OrderStatus
– Collect histograms
SELECT OrderStatus, COUNT(1)
FROM Orders
GROUP BY OrderStatus
I 3760
C 686911
Data Skew Problems
29. 2/21/2013 Confidential - Internal Use Only 29
• Tuning SQL gives more “bang for the buck”
• Make sure you are tuning the correct query
• Use Wait Events and Response Time Analysis
– Locking problems may not be a Query Tuning issue
– Wait events tell you where to start
• Use “Real Execution Plans”
– Get plan_hash_value from V$SESSION, V$SQL
– Pass it to V$SQL_PLAN to get Real Plan
• Ignite – Does All of Above For You
– OEM Does not Give you Enough Details
• SQL Diagramming - “Get it right the First Time”
Takeaway Points
30. Q&A
Thank you for attending.
More questions?
Contact Dean at
DeanRichards@confio.com
Download free trial of Ignite at
http//www.confio.com
2/21/2013 30