Sunteți pe pagina 1din 36

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'; 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

SALE_DATE --------04-JAN-12 02-JAN-12 05-JAN-12 03-JAN-12

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

customers

sales

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

sales

customers

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'; CUSTOMER_NAME ----------------Sunshine State Co Green Valley Inc Sunshine State Co Green Valley Inc

PRODUCT_NAME -----------Chedder Chedder Feta Feta

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;

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';

SQL> 3 4 5 6 7 8

COUNT(*) ---------4 select count(*) from sales, products where sales.product_number=products.product_number;

SQL> 2 3 4

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'; 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

SALE_DATE --------04-JAN-12 02-JAN-12 05-JAN-12 03-JAN-12

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'; 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

SALE_DATE --------04-JAN-12 02-JAN-12 05-JAN-12 03-JAN-12

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'; 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

SALE_DATE --------02-JAN-12 03-JAN-12 04-JAN-12 05-JAN-12

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 CUSTOMER_NAME AMOUNT ----------------- ---------Sunshine State Co 100 Green Valley Inc 200 Sunshine State Co 300 Green Valley Inc 400

Elapsed: 00:00:00.00

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/

S-ar putea să vă placă și