Intro to SQL Tuning
Download
Report
Transcript Intro to SQL Tuning
Brown Bag
Introduction to SQL Tuning
Three essential concepts
Introduction to SQL Tuning
•How to speed up a slow query?
• Find a better way to run the query
• Cause the database to run the query your way
Introduction to SQL Tuning
•How does a database run a SQL query?
• Join order
• Join method
• Access method
Example Query
SQL>
2
3
4
5
6
7
8
9
10
11
select
sale_date, product_name, customer_name, amount
from sales, products, customers
where
sales.product_number=products.product_number and
sales.customer_number=customers.customer_number and
sale_date between
to_date('01/01/2012','MM/DD/YYYY') and
to_date('01/31/2012','MM/DD/YYYY') and
product_type = 'Cheese' and
customer_state = 'FL';
SALE_DATE
--------04-JAN-12
02-JAN-12
05-JAN-12
03-JAN-12
PRODUCT_NAME
-----------Feta
Chedder
Feta
Chedder
CUSTOMER_NAME
AMOUNT
----------------- ---------Sunshine State Co
300
Sunshine State Co
100
Green Valley Inc
400
Green Valley Inc
200
Join Order
•Join Order = order in which tables in from clause are joined
•Two row sources at a time
•Row source:
•Table
•Result of join
•View as tree – execution tree or plan
Join Order – sales, products, customers
join 2
join 1
sales
customers
products
Join Order as Plan
Execution Plan
---------------------------------------------------------0
SELECT STATEMENT
1
0
HASH JOIN
2
1
HASH JOIN
3
2
TABLE ACCESS (FULL) OF 'SALES' (TABLE)
4
2
TABLE ACCESS (FULL) OF 'PRODUCTS' (TABLE
5
1
TABLE ACCESS (FULL) OF 'CUSTOMERS' (TABLE)
Bad Join Order – customers, products, sales
join 2
join 1
customers
sales
products
Cartesian Join – all products to all customers
SQL>
SQL>
SQL>
SQL>
2
3
4
5
6
-- joining products and customers
-- cartesian join
select
product_name,customer_name
from products, customers
where
product_type = 'Cheese' and
customer_state = 'FL';
PRODUCT_NAME
-----------Chedder
Chedder
Feta
Feta
CUSTOMER_NAME
----------------Sunshine State Co
Green Valley Inc
Sunshine State Co
Green Valley Inc
Plan with Cartesian Join
Execution Plan
---------------------------------------------------------0
SELECT STATEMENT Optimizer=ALL_ROWS
1
0
MERGE JOIN (CARTESIAN)
2
1
TABLE ACCESS (FULL) OF 'PRODUCTS' (TABLE)
3
1
BUFFER (SORT)
4
3
TABLE ACCESS (FULL) OF 'CUSTOMERS' (TABLE)
Selectivity
•Selectivity = percentage of rows accessed versus total rows
•Use non-joining where clause predicates
•sale_date, product_type, customer_state
•Compare count of rows with and without non-joining predicates
Count(*) to get selectivity
-- # selected rows
select
count(*)
from sales
where
sale_date between
to_date('01/01/2012','MM/DD/YYYY') and
to_date('01/31/2012','MM/DD/YYYY');
-- total #rows
select
count(*)
from sales;
SQL>
3
4
5
6
7
8
Selectivity of sub-tree
select count(*) from sales, products
where
sales.product_number=products.product_number and
sale_date between
to_date('01/01/2012','MM/DD/YYYY') and
to_date('01/31/2012','MM/DD/YYYY') and
product_type = 'Cheese';
COUNT(*)
---------4
SQL>
2
3
4
select count(*)
from sales, products
where
sales.product_number=products.product_number;
COUNT(*)
---------4
Modifying the Join Order
•Tables with selective predicates first
•Gather Optimizer Statistics
•Estimate Percent
•Histogram on Column
•Cardinality Hint
•Leading Hint
•Break Query into Pieces
Gather Optimizer Statistics
-- 1 - set preferences
begin
DBMS_STATS.SET_TABLE_PREFS(NULL,'SALES','ESTIMATE_PERCENT','10');
DBMS_STATS.SET_TABLE_PREFS(NULL,'SALES','METHOD_OPT',
'FOR COLUMNS SALE_DATE SIZE 254 PRODUCT_NUMBER SIZE 1 '||
'CUSTOMER_NUMBER SIZE 1 AMOUNT SIZE 1');
end;
/
-- 2 - regather table stats with new preferences
execute DBMS_STATS.GATHER_TABLE_STATS (NULL,'SALES');
Cardinality Hint
SQL>
2
3
4
5
6
7
8
9
10
11
select /*+cardinality(sales 1) */
sale_date, product_name, customer_name, amount
from sales, products, customers
where
sales.product_number=products.product_number and
sales.customer_number=customers.customer_number and
sale_date between
to_date('01/01/2012','MM/DD/YYYY') and
to_date('01/31/2012','MM/DD/YYYY') and
product_type = 'Cheese' and
customer_state = 'FL';
SALE_DATE
--------04-JAN-12
02-JAN-12
05-JAN-12
03-JAN-12
PRODUCT_NAME
-----------Feta
Chedder
Feta
Chedder
CUSTOMER_NAME
AMOUNT
----------------- ---------Sunshine State Co
300
Sunshine State Co
100
Green Valley Inc
400
Green Valley Inc
200
Plan with Cardinality hint
Execution Plan
---------------------------------------------------------0
SELECT STATEMENT Optimizer=ALL_ROWS
1
0
HASH JOIN
2
1
HASH JOIN
3
2
TABLE ACCESS (FULL) OF 'SALES' (TABLE)
4
2
TABLE ACCESS (FULL) OF 'PRODUCTS' (TABLE
5
1
TABLE ACCESS (FULL) OF 'CUSTOMERS' (TABLE)
Leading Hint
SQL>
2
3
4
5
6
7
8
9
10
11
select /*+leading(sales) */
sale_date, product_name, customer_name, amount
from sales, products, customers
where
sales.product_number=products.product_number and
sales.customer_number=customers.customer_number and
sale_date between
to_date('01/01/2012','MM/DD/YYYY') and
to_date('01/31/2012','MM/DD/YYYY') and
product_type = 'Cheese' and
customer_state = 'FL';
SALE_DATE
--------04-JAN-12
02-JAN-12
05-JAN-12
03-JAN-12
PRODUCT_NAME
-----------Feta
Chedder
Feta
Chedder
CUSTOMER_NAME
AMOUNT
----------------- ---------Sunshine State Co
300
Sunshine State Co
100
Green Valley Inc
400
Green Valley Inc
200
Break Query Into Pieces
SQL>
2
3
4
5
6
7
8
create global temporary table sales_product_results
(
sale_date date,
customer_number number,
amount number,
product_type varchar2(12),
product_name varchar2(12)
) on commit preserve rows;
Table created.
Break Query Into Pieces
SQL>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
insert /*+append */
into sales_product_results
select
sale_date,
customer_number,
amount,
product_type,
product_name
from sales, products
where
sales.product_number=products.product_number and
sale_date between
to_date('01/01/2012','MM/DD/YYYY') and
to_date('01/31/2012','MM/DD/YYYY') and
product_type = 'Cheese';
4 rows created.
Break Query Into Pieces
SQL>
2
3
4
5
6
select
sale_date, product_name, customer_name, amount
from sales_product_results spr, customers c
where
spr.customer_number=c.customer_number and
c.customer_state = 'FL';
SALE_DATE
--------02-JAN-12
03-JAN-12
04-JAN-12
05-JAN-12
PRODUCT_NAME
-----------Chedder
Chedder
Feta
Feta
CUSTOMER_NAME
AMOUNT
----------------- ---------Sunshine State Co
100
Green Valley Inc
200
Sunshine State Co
300
Green Valley Inc
400
Join Methods
•Join Method = way that data from two sources is joined
•Nested Loops
•Small number of rows in first table
•Unique index on second large table
•Hash Join
•Smaller or equal number of rows in first table
•No index required
Join Method – Nested Loops
Execution Plan
-----------------------------------------------------------------0
SELECT STATEMENT Optimizer=ALL_ROWS
1
0
TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS' (TABLE)
2
1
NESTED LOOPS
3
2
NESTED LOOPS
4
3
TABLE ACCESS (FULL) OF 'SALES' (TABLE)
5
3
TABLE ACCESS (BY INDEX ROWID) OF 'PRODUCTS'
6
5
INDEX (RANGE SCAN) OF 'PRODUCTS_INDEX' (INDEX)
7
2
INDEX (RANGE SCAN) OF 'CUSTOMERS_INDEX' (INDEX)
Join Method – Hash Join
Execution Plan
---------------------------------------------------------0
SELECT STATEMENT Optimizer=ALL_ROWS
1
0
HASH JOIN
2
1
HASH JOIN
3
2
TABLE ACCESS (FULL) OF 'SALES' (TABLE)
4
2
TABLE ACCESS (FULL) OF 'PRODUCTS'
5
1
TABLE ACCESS (FULL) OF 'CUSTOMERS' (TABLE)
Modifying the Join Method
•Hints
•use_hash
•use_nl
•Add Index
•Hash_area_size parameter
Join Methods Hints
/*+ use_hash(products) use_nl(customers) */
Join Methods Indexes
create index products_index on products(product_number);
create index customers_index on customers(customer_number);
Join Methods Hash_Area_Size
NAME
-----------------------------------hash_area_size
sort_area_size
workarea_size_policy
TYPE
----------integer
integer
string
VALUE
--------100000000
100000000
MANUAL
Access Methods
•Access method = way that data is retrieved from table
•Index scan – small number of rows accessed
•Full scan – larger number of rows accessed
Modifying the Access Method
•Set Initialization Parameter
•optimizer_index_caching
•optimizer_index_cost_adj
•db_file_multiblock_read_count
•Set Parallel Degree > 1
•Hints
•Full
•Index
Set Initialization Parameter
alter system
set optimizer_index_cost_adj=1000
scope=both
sid='*';
Set Parallel Degree
alter table sales parallel 8;
Full Scan and Index Hints
/*+ full(sales) index(customers) index(products) */
Conclusion
• Use count queries to determine selective parts of where clause
• Modify the join order, join methods, and access methods using
• Optimizer statistics
• Hints
• Initialization parameters
• Breaking the query into pieces
• Parallel degree
• Indexes
• Compare elapsed time of query with new plan to original
Check For Improved Elapsed Time
SQL> set timing on
SQL>
SQL> select …
… removed for clarity …
SALE_DATE
--------02-JAN-12
03-JAN-12
04-JAN-12
05-JAN-12
PRODUCT_NAME
-----------Chedder
Chedder
Feta
Feta
Elapsed: 00:00:00.00
CUSTOMER_NAME
AMOUNT
----------------- ---------Sunshine State Co
100
Green Valley Inc
200
Sunshine State Co
300
Green Valley Inc
400
Further Reading
• Oracle Database Concepts
• Chapter 7 SQL
• Oracle Database Performance Tuning Guide
• Chapter 11 The Query Optimizer
• Chapter 19 Using Optimizer Hints
• Oracle Database Reference
• Chapter 1 Initialization Parameters
• Oracle Database PL/SQL Packages and Types Reference
• Chapter 141 DBMS_STATS
• Cost-Based Oracle Fundamentals - Jonathan Lewis
• http://www.bobbydurrettdba.com/resources/