Consideration when writing an SQL statement is that it returns a correct result. The second is that it be the most efficient for a given situation. 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.
Remember
that processing SQL is a sequence of Parse (syntax check and object
resolution), Execution (required reads and writes), and Fetch (row results
retrieved, listed, sorted, and returned). SQL “tuning” consists, quite simply,
of reducing one or more of them.
Note:
generally Parse is the greatest time and resource hog. Parse overhead can be
minimized by the use of Procedures, Functions, Packages, Views, etc.
Inadequate
performance can have a significant cost impact on your business. A poor
performing system and application can result in customer dissatisfaction,
reduced productivity, and high costs. It is absolutely critical that the
system’s performance is operating at its peak levels.
Following
are some general tips that often increase SQL statement efficiency. Being
general they may not apply to a particular scenario.
- 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.
- Ensure repeated SQL statements are
writtenabsolutely identicallyto facilitate efficient reuse:
re-parsing can often be avoided for each subsequent use of an identical
statement.
Writing best practices: all SQL verbs in upper-case i.e. SELECT; separate all words with a single space; all SQL verbs begin on a new line; SQL verbs aligned right or left within the initial verb; set and maintain a table alias standard; use table aliases and when a query involves more than one table prefix all column names with their aliases. Whatever you do, be consistent.
- 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.
- Use 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 re-use
or shared.
- 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 same name as a column in
another table. (ORA-00918: COLUMN AMBIGUOUSLY DEFINED)
- Code the query as simply as
possible i.e. no unnecessary columns are selected, no unnecessary GROUP BY
or ORDER BY.
It is the same or faster to SELECT by actual column name(s). The larger the table the more likely the savings.
Use:
SELECT customer_id, last_name, first_name, street, city FROM customer;
Rather than,
SELECT * FROM customer;
- 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 these WHERE clause can be re-written to use an index while
returning the same values. In other words, Do not perform operations on
database objects referenced in the WHERE clause:
Use:
SELECT client, date, amount FROM sales WHERE amount > 0;
Rather than:
SELECT client, date, amount FROM sales WHERE amount != 0;
———————————————
Use:
SELECT account_name, trans_date, amount FROM transaction WHERE account_name LIKE ‘CAPITAL%’;
Rather than:
SELECT account_name, trans_date, amount FROM transaction WHERE SUBSTR(account_name,1,7)=‘CAPITAL’;
———————————————
Use:
SELECT account_name, trans_date, amount FROM transaction WHERE amount > 0;
Rather than:
SELECT account_name, trans_date, amount FROM transaction WHERE amount NOT=0;
———————————————
Use:
SELECT account_name, trans_date, amount FROM transaction WHERE amount < 2000;
Rather than:
SELECT account_name, trans_date, amount FROM transaction WHERE amount + 3000 < 5000;
———————————————
Use:
SELECT account_name, trans_date, amount FROM transaction WHERE account_name = ‘AMEX’ AND account_type = ‘A’;
Rather than:
SELECT account_name, trans_date, amount FROM transaction WHERE account_name|| account_type=‘AMEXA’;
- Don’t forget to tune views. Views
are SELECT statements and can be tuned in just the same way as any other
type of SLECT statement can be. All tuning applicable to any SQL statement
are equally applicable to views.
- 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.
Use:
SELECT city FROM country WHERE city!= ‘Vancouver’ AND city!= ‘Toronto’; GROUP BY city;
Rather than:
SELECT city FROM country GROUP BY city HAVING city!= ‘Vancouver’ AND city!= ‘Toronto’;
- Minimize the number of table
lookups (subquery blocks) in queries, particularly if our statements
include subquery SELECTs or multicolumn UPDATEs.
Use:
SELECT emp_name FROM emp WHERE (emp_cat, sal_range) = (SELECT MAX(category), MAX(sal_range) FROM emp_categories) AND emp_dept = 0020;
Rather than:
SELECT emp_name FROM emp WHERE emp_cat=(SELECT MAX(category) FROM emp_categories) AND emp_range = (SELECT MAX(sal_range) FROM emp_categories) AND emp_dept = 0020;
- When writing a sub-query (a SELECT
statement within the WHERE or HAVING clause of another SQL statement):
- Use a correlated (refers to at
least one value from the outer query) sub-query when the return is
relatively small and/or other criteria are efficient i.e. if the tables
within the sub-query have efficient indexes.
- Use a non-correlated (does not
refer to the outer query) sub-query when dealing with large tables from
which you expect a large return (many rows) and/or if the tables within
the sub-query do not have efficient indexes.
- Ensure that multiple sub-queries
are in the most efficient order.
- Remember that rewriting a
sub-query as a join can sometimes increase efficiency.
- When doing multiple table joins
consider the benefits/costs for each of EXISTS, IN, and table joins.
Depending on your data one or another may be faster.
Note: IN is usually the slowest.
Note: When most of the filter criteria are in the sub-query IN may be more efficient; when most of the filter criteria are in the parent-query EXISTS may be more efficient.
The following queries return 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 AND dept_Cat = ‘A’);
———————————————
SELECT emp_name FROM dept D, emp E WHERE E.dept_no = D.dept_no AND D.dept_cat = ‘A’;
- Where possible use EXISTS rather
than DISTINCT, to avoid full table scan as 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.
Use:
SELECT S.id, S.description FROM small_table S WHERE EXISTS (SELECT NULL FROM big_table B WHERE B.id = S.id);
Rather than:
SELECT DISTINCT S.id, S.description FROM small_table S, big_table B
WHERE S.id = B.id;
- Where possible use a non-column
expression (putting the column on one side of the operator and all the
other values on the other). Non-column expressions are often processed
earlier thereby speeding the query.
Use:
WHERE SALES < 1000/(1 + n);
Rather than:
WHERE SALES + (n * SALES) < 1000;
- Where possible use UNION ALL
rather than using UNION. The UNION clause forces all rows retuned by each
portion of the UNION to be sorted and merged and duplicates to be filtered
out 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.
USE:
SELECT acct_num, balance_amt FROM debit_transactions WHERE tran_date = ’31-DEC-95′
UNION ALL
SELECT acct_num, balance_amt FROM credit_transactions WHERE tran_date = ’31-DE -95′;
Rather than:
SELECT acct_num, balance_amt FROM debit_transactions WHERE tran_date = ’31-DEC-95′
UNION
SELECT acct_num, balance_amt FROM credit_transactions WHERE tran_date = ’31-DE -95′;
- 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 t change your code when new values are allowed in the field.
USE:
SELECT COUNT(DECODE(status,’Y’,’X’,NULL)) Y_count,
COUNT(DECODE(status,’N’,’X’,NULL)) N_count
FROM emp WHERE emp_name LIKE ‘SMITH%’;
Raher than:
SELECT COUNT(*) FROM emp WHERE status = ‘Y’ AND emp_name LIKE ‘SMITH%’;
———————
SELECT COUNT(*) FROM emp WHERE status = ‘N’ AND emp_name LIKE ‘SMITH%’;
- 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.
Use:
SELECT emp_no, emp_name, sal FROM emp WHERE emp_no = ‘123’;
HERE if emp_no indexed numeric, then after implicit conversion query will be:
SELECT emp_no, emp_name, sal FROM emp WHERE emp_no = TO_NUMBER(‘123’);
Thus, index is used in this case.
Don’t use:
SELECT emp_no, emp_name, sal FROM emp WHERE emp_type = 123;
HERE if emp_type is indexed varchar2, then after implicit conversion query will be:
SELECT emp_no, emp_name, sal FROM emp WHERE TO_NUMBER(emp_type) = 123;
Thus, index will not be used in this case.
- The most efficient method for
storing large binary objects, i.e. multimedia objects, is to place them in
the file system and place a pointer in the DB.
- B-Tree Indexes do not store
entries for NULL, so IS NULL is not indexable, but IS NOT NULL is
indexable and thus if a huge table contains very few not null values then
you should go for B-Tree indexes. On the other hand bitmap indexes support
IS NULL condition.
SELECT * FROM big WHERE status IS NOT NULL; (Use B-tree index in this case)
- Avoid using functions on indexed
columns unless a function-based index is created; as it leads to full
table scan even though index exists on the column.
Avoid
using the following:
- Boolean operators >, <,
>=, <=, IS NULL, IS NOT NULL
- NOT IN, !=
- Like ‘%pattern’, not exists
- Calculations on unindexed columns
or (use union instead)
- Having (use a WHERE clause instead
when appropriate)
Do use
the following:
- Enable aliases to prefix all
columns
- Place indexed columns higher in
the WHERE clause
- Use SQL Joins instead of using
sub-queries
- Make the table with the least
number of rows, the driving table, by making it first in the FROM clause
Other
important points for SQL Tuning
- Establish a tuning environment
that reflects your production database
- Establish performance expectations
before you begin
- Always Design and develop with
performance in mind
- Create Indexes to support
selective WHERE clauses and join conditions
- Use concatenated indexes where
appropriate
- Consider indexing more than you
think you should, to avoid table lookups
- Pick the best join method
- Nested loops joins are best for
indexed joins of subsets
- Hash joins are usually the best
choice for “big” joins
- Pick the best join order
- Pick the best “driving” table
- Eliminate rows as early as
possible in the join order
- Use bind variables. Bind variables
are key to application scalability
- Use Oracle hints where appropriate
- Compare performance between
alternative syntax for your SQL statement
- Consider utilizing PL/SQL to
overcome difficult SQL tuning issues
- Consider using third party tools
to make the job of SQL tuning easier
Performing
these steps is easy and provides a tremendous benefit and performance boost.
Follow these simple steps and you can increase your system performance.
Comments
Post a Comment