Вы находитесь на странице: 1из 7

SQL Tuning Tips

SQL is the heart of the Oracle system. You can use many different SQL statements to
achieve the same result. It is often the case that only one statement will be the most
efficient choice in a given situation. The tips below include information about whether
one form of the statement is always MORE efficient or whether each statement is an
alternative and the efficency will vary depending on your application.

Oracle processes SQL in two steps: parsing and execution. Tuning may speed up your
SQL by reducing either parsing, execution or both. Note, tuning SQL should only be
done after your code is working correctly. Be aware that there is an inevitable tug-of-war
between writing efficient SQL and understandable SQL.

TIP 1 (Best Tip): SQL cannot be shared within Oracle unless it is absolutely
identical. Statements must have match exactly in case, white space and
underlying schema objects to be shared within Oracle's memory. Oracle avoids
the parsing step for each subsequent use of an identical statement.

sql> SELECT NAME FROM S_CUSTOMER WHERE ID =


statement to match
212;
sql> SELECT NAME FROM s_customer WHERE ID = 212; lower case
sql> SELECT NAME FROM S_CUSTOMER WHERE
white space
ID=212;
sql> SELECT NAME
FROM S_CUSTOMER white space
WHERE ID=212;

o Use SQL standards within an application. Rules like the following are
easy to implement and will allow MORE sharing within Oracle's memory.

- Using a single case for all SQL verbs


- Beginning all SQL verbs on a new line
- Right or left aligning verbs within the initial SQL verb
- Separating all words with a single space

1
o Use bind variables. The values of bind variables do not need to be the
same for two statements to be considered identical. Bind variables are not
substituted until a statement has been successfully parsed.

SELECT * FROM emp WHERE emp_no


Sharable SQL Bind value: 123
= :B1;
SELECT * FROM emp WHERE emp_no
Bind value: 987
= :B1;
SELECT * FROM emp WHERE emp_no =
Non-sharable SQL
123;
SELECT * FROM emp WHERE emp_no =
987;

o Use a standard approach to table aliases. If two identical SQL statements


vary because an identical table has two different aliases, then the SQL is
different and will not be shared.

o Use table aliases and prefix all column names by their aliases when
MORE than one table is involved in a query. This reduces parse time
AND prevents future syntax errors if someone adds a column to one of the
tables with the same name as a column in another table. (ORA-00918:
COLUMN AMBIGUOUSLY DEFINED)

TIP 2: Beware of WHERE clauses which do not use indexes at all. Even if there
is an index over a column that is referenced by a WHERE clause included in this
section, Oracle will ignore the index. All of these WHERE clauses can be re-
written to use an index while returning the same values. In other words, don't
perform operations on database objects referenced in the WHERE clause.

Do Not Use Use


SELECT account_name, trans_date, SELECT account_name, trans_date,
amount amount
FROM transaction FROM transaction
WHERE SUBSTR(account_name,1,7) WHERE account_name LIKE
= 'CAPITAL'; 'CAPITAL%';

2
SELECT account_name, trans_date, SELECT account_name, trans_date,
amount amount
FROM transaction FROM transaction
WHERE account_name = NVL ( WHERE account_name LIKE NVL (
:acc_name, account_name); :acc_name, '%');
SELECT account_name, trans_date,
SELECT account_name, trans_date,
amount
amount
FROM transaction
FROM transaction
WHERE trans_date BETWEEN
WHERE TRUNC (trans_date) =
TRUNC (SYSDATE) AND TRUNC
TRUNC (SYSDATE);
(SYSDATE) + .99999;
SELECT account_name, trans_date, SELECT account_name, trans_date,
amount amount
FROM transaction FROM transaction
WHERE account_name || account_type WHERE account_name = 'AMEX'
= 'AMEXA'; AND account_type = 'A';
SELECT account_name, trans_date, SELECT account_name, trans_date,
amount amount
FROM transaction FROM transaction
WHERE amount + 3000 < 5000; WHERE amount < 2000;
SELECT account_name, trans_date, SELECT account_name, trans_date,
amount amount
FROM transaction FROM transaction
WHERE amount != 0; WHERE amount > 0;
SELECT account_name, trans_date, SELECT account_name, trans_date,
amount amount
FROM transaction FROM transaction
WHERE amount NOT = 0; WHERE amount > 0;

TIP 3: Don't forget to tune views. Views are SELECT statements and can be
tuned in just the same way as any other type of SELECT statement can be. All
tuning applicable to any SQL statement are equally applicable to views.

TIP 4: Avoid including a HAVING clause in SELECT statements. The


HAVING clause filters selected rows only after all rows have been fetched.
Using a WHERE clause helps reduce overheads in sorting, summing, etc.
HAVING clauses should only be used when columns with summary operations
applied to them are restricted by the clause.

Do Not Use Use

3
SELECT region, AVG (loc_size) SELECT region, AVG (loc_size)
FROM location FROM location
GROUP BY region WHERE region != 'SYDNEY'
HAVING region != 'SYDNEY' AND region != 'PERTH';
AND region != 'PERTH'; GROUP BY region;

TIP 5: Minimize the number of table lookups (subquery blocks) in queries,


particularly if your statements include subquery SELECTs or multicolumn
UPDATEs.

SELECT emp_name
FROM emp
WHERE emp_cat = (SELECT MAX (category)
Separate
FROM emp_categories)
Subqueries
AND emp_range = (SELECT MAX (sal_range)
FROM emp_categories)
AND emp_dept = 0020;
SELECT emp_name
FROM emp
Combined WHERE (emp_cat, sal_range)
Subqueries = (SELECT MAX (category), MAX (sal_range)
FROM emp_categories)
AND emp_dept = 0020;

TIP 6: Consider the alternatives EXISTS, IN and table joins when doing multiple
table joins. None of these are consistently faster; it depends on your data. If there
is a poor performer here, it's likely the IN clause.

(Note, this query returns the employee names from each department in
department category 'A'.)
SELECT emp_name
FROM emp E
WHERE EXISTS ( SELECT 'X'
FROM dept
WHERE dept_no = E.dept_no
AND dept_cat = 'A');
SELECT emp_name
FROM emp E
WHERE dept_no IN ( SELECT dept_no
FROM dept
WHERE dept_no = E.dept_no

4
AND dept_cat = 'A');
SELECT emp_name
FROM dept D, emp E
WHERE E.dept_no = D.dept_no
AND D.dept_cat = 'A';

TIP 7: Avoid joins that require the DISTINCT qualifier on the SELECT list in
queries which are used to determine information at the owner end of a one-to-
many relationship. The DISTINCT operator causes Oracle to fetch all rows
satisfying the table join and then sort and filter out duplicate values. EXISTS is a
faster alternative, because the Oracle optimizer realizes when the subquery has
been satisfied once, there is no need to proceed further and the next matching row
can be fetched.

(Note: This query returns all department numbers and names which have at least
one employee.)
Do Not Use Use
SELECT dept_no, dept_name
SELECT DISTINCT dept_no, FROM dept D
dept_name WHERE EXISTS (
FROM dept D, SELECT 'X'
emp E FROM emp E
WHERE D.dept_no = E.dept_no; WHERE E.dept_no =
D.dept_no);

TIP 8: Consider whether a UNION ALL will suffice in place of a UNION. The
UNION clause forces all rows returned by each portion of the UNION to be
sorted and merged and duplicates to be filtered before the first row is returned. A
UNION ALL simply returns all rows including duplicates and does not have to
perform any sort, merge or filter. If your tables are mutually exclusive (include
no duplicate records), or you don't care if duplicates are returned, the UNION
ALL is much MORE efficient.

UNION UNION ALL


SELECT acct_num, balance_amt SELECT acct_num, balance_amt
FROM debit_transactions FROM debit_transactions
WHERE tran_date = '31-DEC-95' WHERE tran_date = '31-DEC-95'
UNION UNION ALL
SELECT acct_num, balance_amt SELECT acct_num, balance_amt
FROM credit_transactions FROM credit_transactions
WHERE tran_date = '31-DEC-95'; WHERE tran_date = '31-DEC-95';

5
TIP 9: Consider using DECODE to avoid having to scan the same rows
repetitively or join the same table repetitively. Note, DECODE is not necessarily
faster as it depends on your data and the complexity of the resulting query. Also,
using DECODE requires you to change your code when new values are allowed
in the field.

SELECT COUNT(*)
FROM emp
WHERE status = 'Y'
AND emp_name LIKE 'SMITH%';
----------
SELECT COUNT(*)
FROM emp
WHERE status = 'N'
AND emp_name LIKE 'SMITH%';
SELECT COUNT(DECODE(status, 'Y', 'X', NULL)) Y_count,
COUNT(DECODE(status, 'N', 'X', NULL)) N_count
FROM emp
WHERE emp_name LIKE 'SMITH%';

TIP 10: Oracle automatically performs simple column type conversions (or
casting) when it compares columns of different types. Depending on the type of
conversion, indexes may not be used. Make sure you declare your program
variables as the same type as your Oracle columns, if the type is supported in the
programming language you are using.

Datatype
of
Index
field in Your Query After Implicit Conversion
Used?
where
clause
SELECT ... SELECT ...
emp_no
FROM emp FROM emp
indexed YES
WHERE emp_no = WHERE emp_no =
numeric
'123'; TO_NUMBER('123');
SELECT ... SELECT ...
emp_type
FROM emp FROM emp NO!
indexed
WHERE emp_type WHERE TO_NUMBER (emp_type)
varchar2
= 123; = 123;

6
7

Вам также может понравиться