| Lesson 5 | Detecting full-table scans with EXPLAIN PLAN |
| Objective | Detect a full-table scan by reading an execution plan. |
A full-table scan occurs when Oracle reads table blocks to evaluate a query instead of using an index
to navigate directly to a small set of rows. In an execution plan, this typically appears as
TABLE ACCESS FULL.
In Oracle 23ai, full-table scans are still used and often appropriate. The tuning task is not “eliminate all full scans,” but to detect unexpected full scans and decide whether the optimizer made the right trade-off.
TABLE ACCESS FULL — Oracle will scan the table (or a large portion of it).
EXPLAIN PLAN records the optimizer’s predicted plan into a plan table. A common rendering approach is
DBMS_XPLAN.DISPLAY.
EXPLAIN PLAN FOR
SELECT *
FROM customers
WHERE last_name = 'Smith';
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, NULL, 'BASIC'));
If the output includes TABLE ACCESS FULL, you have detected a full-table scan for the statement.
The next step is to determine whether it is appropriate.
A simplified example (your output will vary):
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | 250 (100)| |
| 1 | TABLE ACCESS FULL | CUSTOMERS | 1000 | 5000 | 250 (50)| 00:00:03 |
--------------------------------------------------------------------------------
The key indicator is the Operation column. When you see
TABLE ACCESS FULL, Oracle expects scanning the table to be the best access path (given its estimates).
EXPLAIN PLAN shows what the optimizer would do, given the environment at explain time.
To see what actually executed (including runtime row counts), execute the SQL and display the cursor plan.
-- Run the query (example)
SELECT *
FROM customers
WHERE last_name = 'Smith';
-- Then display the actual plan for the last cursor in your session
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));
Use DISPLAY_CURSOR when you are diagnosing real performance, because it helps you spot cardinality
misestimates (estimated vs actual rows) that frequently lead to unexpected full scans.
A full scan is often the correct plan choice, including:
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => 'HR',
tabname => 'CUSTOMERS'
);
END;
/
CREATE INDEX idx_customers_last_name
ON customers(last_name);
DISPLAY_CURSOR.
Oracle 23ai introduces new features and access paths, but it does not remove the need to verify execution plans. For example:
Unexpected full-table scans can cause slow-downs because they drive high I/O and CPU, especially when the query needs only a small subset of rows. Oracle may perform multiblock reads for table scans; in modern Oracle releases, scan behavior is influenced by storage, caching, and system configuration. The key practice is: detect the scan in the plan, confirm whether it is justified, and fix the root cause (indexing, statistics, or query shape). Note that modern Oracle versions optimize multiblock reads automatically; older manual tuning of parameters like DB_FILE_MULTIBLOCK_READ_COUNT is generally less necessary.
The key practice is: detect the scan in the plan, confirm whether it is justified, and fix the root cause (indexing, statistics, or query shape).
STATUS on POPULATION).
TABLE ACCESS FULL and scan blocks to evaluate STATUS = 'Convicted Felon'.
INDEX RANGE SCAN) instead of scanning the table.
Full-table scans can be appropriate and even faster than index access in these common cases:
The goal is to detect unexpected full scans and determine whether creating an index (or improving statistics) would allow Oracle to choose a more selective access path.
Execution plans are hierarchical. Modern Oracle environments typically render plans using DBMS_XPLAN
(instead of writing a formatting query against PLAN_TABLE).
EXPLAIN PLAN FOR
SELECT e.ename, d.dname
FROM emp e
JOIN dept d ON d.deptno = e.deptno
ORDER BY d.dname;
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY(NULL, NULL, 'TYPICAL'));
When diagnosing a slowdown, prefer the executed cursor plan:
SELECT *
FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));
In the next lesson, you will identify nested access paths (multiple tables, joins, and how access choices compose into a complete plan).