You are on page 1of 11

Tips for writing more efficient SQL

Space doesn't permit me to discuss every detail of Oracle tuning, but I can share some general
rules for writing efficient SQL in Oracle regardless of the optimizer that is chosen. These
rules may seem simplistic but following them in a diligent manner will generally relieve more
than half of the SQL tuning problems that are experienced:

Rewrite complex subqueries with temporary tables - Oracle created the global
temporary table (GTT) and the SQL WITH operator to help divide-and-conquer
complex SQL sub-queries (especially those with with WHERE clause subqueries,
SELECT clause scalar subqueries and FROM clause in-line views). Tuning SQL
with temporary tables (and materializations in the WITH clause) can result in amazing
performance improvements.

Use minus instead of EXISTS subqueries - Some say that using the minus operator
instead of NOT IN and NOT Exists will result in a faster execution plan.

Use SQL analytic functions - The Oracle analytic functions can do multiple
aggregations (e.g. rollup by cube) with a single pass through the tables, making them
very fast for reporting SQL.

Re-write NOT EXISTS and NOT EXISTS subqueries as outer joins - In many
cases of NOT queries (but ONLY where a column is defined as NULL), you can rewrite the uncorrelated subqueries into outer joins with IS NULL tests. Note that this
is a non-correlated sub-query, but it could be re-written as an outer join.

select book_key from book


where
book_key NOT IN (select book_key from sales);

Below we combine the outer join with a NULL test in the WHERE clause without using a
sub-query, giving a faster execution plan.
select b.book_key from book b, sales s
where
b.book_key = s.book_key(+)
and
s.book_key IS NULL;

Index your NULL values - If you have SQL that frequently tests for NULL, consider
creating an index on NULL values. To get around the optimization of SQL queries
that choose NULL column values (i.e. where emp_name IS NULL), we can create a
function-based index using the null value built-in SQL function to index only on the
NULL columns.

Leave column names alone - Never do a calculation on an indexed column unless


you have a matching function-based index (a.k.a. FBI). Better yet, re-design the
schema so that common where clause predicates do not need transformation with a
BIF:

where salary*5
> :myvalue
where substr(ssn,7,4)
= "1234"
where to_char(mydate,mon) = "january"

Avoid the use of NOT IN or HAVING. Instead, a NOT EXISTS subquery may run
faster (when appropriate).

Avoid the LIKE predicate = Always replace a "like" with an equality, when
appropriate.

Never mix data types - If a WHERE clause column predicate is numeric, do not to
use quotes. For char index columns, always use quotes. There are mixed data type
predicates:

where cust_nbr = "123"


where substr(ssn,7,4) = 1234

Use decode and case - Performing complex aggregations with the "decode" or "case"
functions can minimize the number of times a table has to be selected.

Don't fear full-table scans - Not all OLTP queries are optimal when they uses
indexes. If your query will return a large percentage of the table rows, a full-table
scan may be faster than an index scan. This depends on many factors, including your
configuration (values for db_file_multiblock_read_count, db_block_size), query
parallelism and the number of table/index blocks in the buffer cache.

Oracle SQL Performance Tuning


1. SQL Performance Tuning team recommends using COUNT(1) instead COUNT(*) for
SQL query performance optimization.
Example:
Do not use:
SELECT COUNT(*)
FROM master;

1 Do not use:
2 SELECT COUNT(*)
3 FROM master;
4
5 Use:
6 SELECT COUNT(1)
7 FROM master;
2. Never compare NULL with NULL. Consider that NULL is not like an empty string or like
the number 0. Also NULL can not be not equal to NULL (NULL != NULL).

Example:
SELECT COUNT(1)
FROM all_users
WHERE 1 = 1;

SELECT COUNT(1)
1
FROM all_users
2
WHERE 1 = 1;
3
4
Result: With condition 1 = 1 you will get result. But in this case you can also omit
5
condition 1 = 1.
6
7
SELECT COUNT(1)
8
FROM all_users
9
WHERE NULL = NULL;
10
11
Result: With condition NULL = NULL you will get result 0.
12
13
SELECT COUNT(1)
14
FROM all_users
15
WHERE NULL != NULL;
16
17
Result: With condition NULL != NULL you will also get result 0.
3. If you are using more than one table, make sure to use table aliases.
Example:
SELECT COUNT(1)
FROM master m, detail d
WHERE m.id = d.master_id;

1 SELECT COUNT(1)
2 FROM master m, detail d
3 WHERE m.id = d.master_id;
4. It is good practice to use table column names in an SQL query. This way the SQL
statements will be more readable, but that is not the main reason. Example: If in INSERT
statement you use SELECT * FROM x and at some point you add a new column in table x,
SQL will return an error. The third reason why it is better to use table column names is to
reduce network traffic.
5. In WHERE statements make sure to compare string with string and number with number, for
optimal SQL query performance.
Example:

Note: Column id is NUMBER Data Type.


Do not use:
SELECT id, apn, charging_class
FROM master
WHERE id = '4343';

1 Do not use:
2 SELECT id, apn, charging_class
3 FROM master
4 WHERE id = '4343';
5
6 Use:
7 SELECT id, apn, charging_class
8 FROM master
9 WHERE id = 4343;
6. Do not change column values in WHERE statements if possible, recommended by SQL
Performance Tuning.
Example:
Do not use:
WHERE SUBSTR(a.serial_id, INS
Use:

Do not use:
1
WHERE SUBSTR(a.serial_id, INSTR(b.serial_id, ',') - 1) = SUBSTR(b.serial_id,
2
INSTR(b.serial_id, ',') - 1)
3
4
Use:
5
WHERE a.serial_id = b.serial_id
7. Avoid using complex expressions.
Examples:
Avoid:
WHERE serial_id = NVL(:a1, ser
WHERE NVL(serial_id,-1) = ( 1,

1 Avoid:
2 WHERE serial_id = NVL(:a1, serial_id)
3
4 WHERE NVL(serial_id,-1) = ( 1, etc...)

8. If you need to use SQL functions on join predicates that is okay, but do not use them with
indexed table columns.
9. EXISTS vs. IN for sub queries
If the selective predicate is in the sub query, then use IN. If the selective predicate is in the
parent query, then use EXISTS.
10. RETURNING clause
INSERT, UPDATE or DELETE can be used with RETURNING clause when appropriate.
This way the number of calls to the database are reduced.
11. SQL Performance Tuning recommends using CASE statements. It is more efficient to run
a single SQL statement, rather than two separate SQL statements.
Example:
Do not use:
SELECT COUNT (1)
FROM emp
WHERE salary <= 1000;

1 Do not use:
2 SELECT COUNT (1)
3 FROM emp
4 WHERE salary <= 1000;
5
6 SELECT COUNT (1)
7 FROM emp
8 WHERE salary BETWEEN 1000 AND 2000;
9
10 Use:
11 SELECT COUNT (CASE WHEN salary <= 1000
12
THEN 1 ELSE null END) count_1,
13
COUNT (CASE WHEN salary BETWEEN 1001 AND 2000
14
THEN 1 ELSE null END) count_2
15 FROM emp;
12. Use UNION ALL instead of UNION, if possible
Example:
Do not use:
SELECT id, name
FROM emp_bmw
UNION

1 Do not use:
2 SELECT id, name
3 FROM emp_bmw

4 UNION
5 SELECT id, name
6 FROM emp_bmw_welt
7
8 Use:
9 SELECT id, name
10 FROM emp_bmw
11 UNION ALL
12 SELECT id, name
13 FROM emp_bmw_welt
13. SQL Performance Tuning recommends to use minimal number of sub queries, if possible.
Example:
Do not use:
SELECT id, manufacturer, mode
FROM cars
WHERE price = ( SELECT MAX(

1 Do not use:
2 SELECT id, manufacturer, model
3 FROM cars
4 WHERE price = ( SELECT MAX(price)
5
FROM cars_bmw
6
)
7
AND year = ( SELECT MAX(year)
8
FROM cars_bmw
9
)
10
11 Use:
12 SELECT id, manufacturer, model
13 FROM cars
14 WHERE (price, year) = ( SELECT MAX(price), MAX(year)
15
FROM cars_bmw
16
)
14. SQL Performance Tuning recommends when cretin data are used frequently, it is a good
idea to store them into intermediate tables.
15. SQL Performance Tuning OR vs. IN.
Our tests showed that using IN in WHERE condition is a little faster then using OR.
Example:
Do not use:
SELECT *
FROM CDRS_NR
WHERE RECORD_TYPE = 'MTC

1 Do not use:
2 SELECT *
3 FROM CDRS_NR
4 WHERE RECORD_TYPE = 'MTC'
5
OR RECORD_TYPE = 'MOC'
6
OR RECORD_TYPE = 'SMSO'
7
8 Use:
9 SELECT *
10 FROM CDRS_NR
11 WHERE RECORD_TYPE IN ('MTC', 'MOC', 'SMSO')

SQL Tuning/SQL Optimization Techniques:

1) The sql query becomes faster if you use the actual columns names in SELECT statement
instead of than '*'.
For Example: Write the query as
SELECT id, first_name, last_name, age, subject FROM student_details;

Instead of:
SELECT * FROM student_details;

2) HAVING clause is used to filter the rows after all the rows are selected. It is just like a
filter. Do not use HAVING clause for any other purposes.
For Example: Write the query as
SELECT subject, count(subject)
FROM student_details
WHERE subject != 'Science'
AND subject != 'Maths'
GROUP BY subject;

Instead of:
SELECT subject, count(subject)
FROM student_details
GROUP BY subject
HAVING subject!= 'Vancouver' AND subject!= 'Toronto';

3) Sometimes you may have more than one subqueries in your main query. Try to minimize
the number of subquery block in your query.
For Example: Write the query as
SELECT name
FROM employee
WHERE (salary, age ) = (SELECT MAX (salary), MAX (age)
FROM employee_details)
AND dept = 'Electronics';

Instead of:
SELECT name
FROM employee
WHERE salary = (SELECT MAX(salary) FROM employee_details)
AND age = (SELECT MAX(age) FROM employee_details)
AND emp_dept = 'Electronics';

4) Use operator EXISTS, IN and table joins appropriately in your query.


a) Usually IN has the slowest performance.
b) IN is efficient when most of the filter criteria is in the sub-query.
c) EXISTS is efficient when most of the filter criteria is in the main query.
For Example: Write the query as
Select * from product p
where EXISTS (select * from order_items o
where o.product_id = p.product_id)

Instead of:
Select * from product p
where product_id IN
(select product_id from order_items

5) Use EXISTS instead of DISTINCT when using joins which involves tables having one-tomany relationship.
For Example: Write the query as
SELECT d.dept_id, d.dept
FROM dept d
WHERE EXISTS ( SELECT 'X' FROM employee e WHERE e.dept = d.dept);

Instead of:
SELECT DISTINCT d.dept_id, d.dept
FROM dept d,employee e
WHERE e.dept = e.dept;

6) Try to use UNION ALL in place of UNION.


For Example: Write the query as
SELECT id, first_name
FROM student_details_class10
UNION ALL
SELECT id, first_name
FROM sports_team;

Instead of:
SELECT id, first_name, subject
FROM student_details_class10
UNION
SELECT id, first_name
FROM sports_team;

7) Be careful while using conditions in WHERE clause.


For Example: Write the query as
SELECT id, first_name, age FROM student_details WHERE age > 10;

Instead of:
SELECT id, first_name, age FROM student_details WHERE age != 10;

Write the query as


SELECT id, first_name, age
FROM student_details
WHERE first_name LIKE 'Chan%';

Instead of:
SELECT id, first_name, age
FROM student_details
WHERE SUBSTR(first_name,1,3) = 'Cha';

Write the query as


SELECT id, first_name, age
FROM student_details
WHERE first_name LIKE NVL ( :name, '%');

Instead of:
SELECT id, first_name, age
FROM student_details
WHERE first_name = NVL ( :name, first_name);

Write the query as


SELECT product_id, product_name
FROM product
WHERE unit_price BETWEEN MAX(unit_price) and MIN(unit_price)

Instead of:
SELECT product_id, product_name
FROM product
WHERE unit_price >= MAX(unit_price)
and unit_price <= MIN(unit_price)

Write the query as


SELECT id, name, salary
FROM employee
WHERE dept = 'Electronics'
AND location = 'Bangalore';

Instead of:
SELECT id, name, salary
FROM employee
WHERE dept || location= 'ElectronicsBangalore';

Use non-column expression on one side of the query because it will be processed earlier.
Write the query as
SELECT id, name, salary
FROM employee
WHERE salary < 25000;

Instead of:
SELECT id, name, salary
FROM employee
WHERE salary + 10000 < 35000;

Write the query as


SELECT id, first_name, age
FROM student_details
WHERE age > 10;

Instead of:
SELECT id, first_name, age
FROM student_details
WHERE age NOT = 10;

8) Use DECODE to avoid the scanning of same rows or joining the same table repetitively.
DECODE can also be made used in place of GROUP BY or ORDER BY clause.
For Example: Write the query as
SELECT id FROM employee
WHERE name LIKE 'Ramesh%'
and location = 'Bangalore';

Instead of:
SELECT DECODE(location,'Bangalore',id,NULL) id FROM employee
WHERE name LIKE 'Ramesh%';

9) To store large binary objects, first place them in the file system and add the file path in the
database.
10) To write queries which provide efficient performance follow the general SQL standard
rules.
a) Use single case for all SQL verbs
b) Begin all SQL verbs on a new line
c) Separate all words with a single space
d) Right or left aligning verbs within the initial SQL verb