2. Objectives
At the end of this training, you will be able to:
• Understand best practices in SQL and PL/SQL
3. Agenda
– Indexes
– Logical tuning
– Benefits of using exists
– Avoid distinct
– Know your data, know your query
– Check code behind views
4. PL/SQL Tuning for performance- Indexes
• Indexes help Oracle find rows easily as rowid references are stored in
Indexes.
• Similar to reading from a book
Guidelines for creating indexes
• Do create indexes on
– Columns frequently used in WHERE clause
– Columns used in joins
• Avoid creating indexes on
– Columns whose data is updated frequently
Indexes – not too many, not too few
Create indexes only when it is absolutely necessary. While indexes aid in improving the
performance of queries, too many indexes on a table could drastically slow down the
inserts and updates to the table.
5. PL/SQL Tuning for performance- Indexes
Note: Although it is not a standard practice to create indexes on platform tables (starting
with SI_), sometimes when performance issues are seen it might require an index to be
created. In this case, ensure that care is taken to recreate the indexes after each
platform upgrade.
Guidelines for indexes
• When creating composite indexes, the order of columns is important. The
column that is more frequently queried must be put first. If the columns have a
parent-child relation, then the parent column should come first.
Script : IndexBestPractices.sql
– Missing leading index column in join Can’t use index
• Eg: Composite index on STATE, CITY
where state = 'TX' [Index used]
where city = 'DALLAS' [Index Not Used]
where state = 'TX' and city = 'DALLAS' [Index Used]
6. PL/SQL Tuning for performance- Indexes
– NOT, != and <> disable index use
where state not in ('TX', 'FL','OH')[Index Not used]
where state != 'TX' [Index Not used]
Instead try to use IN or = whenever possible
– NULL value references can never use indexes
where state IS NULL [Index Not used]
where state IS NOT NULL [Index Not used]
– Expression references can never use indexes
where substr(city,1,3) = 'DAL' [Index Not used]
where city like 'DAL%' [Index Used]
where city || state = 'DALLASTX' [Index Not used]
where salary * 12 >= 24000 [Index Not used]
where salary >= 2000 [Index Used]
7. PL/SQL Tuning for performance- Indexes
• Avoid using functions on table columns in WHERE clause, instead use
local variables.
• Functions on indexed columns prevent Index scans, unless there is a
function based index on the column.
• DO NOT: “where trunc(created_date) > …”
• BETTER: “where created_date > “
• DO NOT: select * from accounts where account_status
= get_system_constants(‘SUSPENDED STATUS’);
• BETTER: l_suspended_status :=
get_system_constants(‘SUSPENDED STATUS’);
select * from accounts where account_status =
l_suspended_status;
8. PL/SQL Tuning for performance – The benefits of using EXISTS
Lesson Learned
In a query joining multiple tables if some tables are not represented in the SELECT clause
(ie : columns from those tables are not in SELECT clause) those tables can be moved from
JOIN to EXISTS.
EXISTS is always better than using JOIN(in some cases) or IN. This is because once
Oracle finds the first record that fulfills the subquery in the EXISTS clause, it does not
bother to check the remaining records.
Consider the below query-
SELECT user_name
FROM users
WHERE user_id IN (SELECT user_id
FROM user_org_roles)
FOR EACH user_id IN USERS
FOR EACH user_id IN USER_ORG_ROLES
IF USERS.user_id = USER_ORG_ROLES.user_id THEN
TRUE;
END IF;
END LOOP;
END LOOP;
100 users x 1000 org roles = 100,000
comparisons!
9. PL/SQL Tuning for performance – The benefits of using EXISTS
Rewriting using EXISTS
SELECT a.user_name
FROM users a
WHERE EXISTS (SELECT b.user_id
FROM user_org_roles b
WHERE b.user_id = a.user_id)
FOR EACH user_id IN USERS
IF user_id EXISTS in USER_ORG_ROLES THEN
RETURN row;
END IF;
END LOOP;
100 users = 100 comparisons!
10. PL/SQL Tuning for performance – Avoid DISTINCT
Reqmt : Users in organization 100000
SELECT u.user_id
, u.user_name
FROM users u
, user_org_roles r
WHERE u.user_id = r.user_id
AND r.org_id = 100000;
USER_ID USER_NAME
------------ -------------------------------
100001 JOHN DOE
100001 JOHN DOE
Lazy developer approach
SELECT DISTINCT
u.user_id
, u.user_name
FROM users u
, user_org_roles r
WHERE u.user_id = r.user_id
AND r.org_id = 100000;
USER_ID USER_NAME
------------ -------------------------------
100001 JOHN DOE
11. PL/SQL Tuning for performance –Avoid DISTINCT
• DISTINCT
– Fetch All Rows satisfying the join
– Sort and Filter duplicate values
– Uses TEMP tablespace if resultset is large
• Better way to fetch unique values using EXISTS
SELECT u.user_id
, u.user_name
FROM users u
WHERE EXISTS ( SELECT 1
FROM user_org_roles r
WHERE u.user_id = r.user_id
AND r.org_id = 100000
)
12. SQL Tuning – HAVING vs WHERE
• Use HAVING only to filter on aggregate functions (count, sum, avg etc)
• Use WHERE to filter on table columns
Script : SQLBestPractices.sql
Least Efficient:
The statement below is a syntactically correct statement. It will produce
the expected result. However filter is done after all the sorting and
counting is complete.
SELECT job, city, state, COUNT(empno) FROM city_data
GROUP BY job, city, state
HAVING job='CLERK';
Correct Usage
SELECT job, city, state, COUNT(empno) FROM city_data
WHERE job='CLERK'
GROUP BY job, city, state;
13. SQL Tuning – UNION vs UNION ALL
• The UNION operation sorts the result set to eliminate duplicate rows
• UNION ALL includes duplicate rows and does not require a sort.
• Unless you require that the duplicate rows be eliminated, or you are certain
that the member queries do not produce duplicate data, use UNION ALL
SELECT empno, ename, deptno FROM emp_10
UNION
SELECT empno, ename, deptno FROM emp_20;
--Better performance
SELECT empno, ename, deptno FROM emp_10
UNION ALL
SELECT empno, ename, deptno FROM emp_20;
14. PL/SQL coding guidelines - General
– Use equality first.
– Use range operators only where equality does not apply.
– Avoid use of negatives in the form of “ != ” or ” NOT”.
– Avoid LIKE pattern matching.
– Try to retrieve specific rows and in small numbers.
– Filter from large tables first to reduce rows joined. Retrieve tables
in order from the most highly filtered table downwards; preferably
the largest table has the most filtering applied.
– The most highly filtered table is the table having the smallest
percentage of its rows retrieved, preferably the largest table.
– Use indexes wherever possible except for very small tables.
– Let the Optimizer do its job.
15. PL/SQL coding guidelines - General
• Modularize with top-down design; keep executable sections small and
easy to read
• Modularize to reduce complexity, make your tasks manageable, and
make your resulting code maintainable
• Avoid multiple RETURN statements in executable section, instead
save values in variables & return the variable
• Avoid lengthy sections of code, and spaghetti code
• Code sections should be self documenting
• Avoid any code redundancies
16. PL/SQL Tuning for performance
• There will always be many ways to write a query or PL/SQL program
to fulfill a requirement.
• By applying the above concepts and using iterative methods you can
figure out which method yields THE BEST performance.