Advertisement Space

Why is my query slow?

The execution plan is your window into how Oracle actually runs a query. Understanding it is the single most important skill for Oracle performance tuning. Every DBA and developer should know how to read one.

Recipe #1 - EXPLAIN PLAN

The quickest way to see а plan without actually running the query:

EXPLAIN PLAN FOR
SELECT e.ename, d.dname
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE e.sal > 2000;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);

Recipe #2 - Actual execution statistics

EXPLAIN PLAN shows the estimated plan. To see what actually happened, use GATHER_PLAN_STATISTICS:

SELECT /*+ GATHER_PLAN_STATISTICS */ e.ename, d.dname
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE e.sal > 2000;

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));

This shows actual row counts alongside estimates, making it easy to spot cardinality misestimates.

Recipe #3 - Key things to look for

The most important columns in an execution plan:

Common operations ranked from best to worst:

INDEX UNIQUE SCAN      — Best: single row lookup via primary key
INDEX RANGE SCAN       — Good: finding a range of rows via index
INDEX FULL SCAN        — OK: scanning entire index in order
INDEX FAST FULL SCAN   — OK: scanning entire index without order
TABLE ACCESS FULL      — Expensive: reading every row in the table

Recipe #4 - Find the execution plan of a running query

If a query is already running and you need its plan:

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&sql_id'));

You can find the sql_id from v$session or v$sql:

SELECT sql_id, sql_text
FROM v$sql
WHERE sql_text LIKE '%your_table%'
  AND sql_text NOT LIKE '%v$sql%';

Recipe #5 - Compare plans with DBMS_XPLAN.DISPLAY_AWR

For historical analysis, retrieve plans from the AWR repository:

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('&sql_id'));

Learning to read execution plans takes practice, but it's the most direct way to understand why a query is fast or slow.