| Lesson 7 | The Oracle cost-based optimizer |
| Objective | Describe the cost-based optimizer used in Oracle AI Database 26ai. |
Oracle SQL describes a required result without normally specifying every operation used to produce it. The cost-based optimizer evaluates ways to access and combine data, estimates their resource requirements, and selects an execution plan. Understanding those estimates helps explain why a query uses an index, scans a table, or selects a particular join method.
The practical question in Oracle AI Database 26ai is how to give the optimizer useful information and validate its decisions. Rule-based optimization became unsupported in Oracle Database 10g Release 1, not 11g. It is historical background, not a supported alternative to enable when statistics are missing or a query performs poorly.
The previous lesson explained optimization goals such as ALL_ROWS and FIRST_ROWS_n. This lesson examines the information behind the decision: statistics, estimated row counts, access structures, and CPU and I/O work. A plan is an informed prediction, so runtime evidence remains necessary when evaluating performance.
A statement can often produce the same result through different operations. Oracle might scan a table, locate index entries and then retrieve table rows, change the order of joins, or transform a query block into an equivalent form. The optimizer compares considered alternatives within the relevant optimizer environment.
Three components provide a useful explanation:
These components interact. They are not a promise that every possible plan is exhaustively evaluated or that each SQL execution repeats full optimization. A compatible cached cursor can be reused. Conversely, changes in the optimizer environment or relevant objects can affect later plan selection.
The chosen plan is not guaranteed to be the fastest plan under every execution condition. Estimates can be inaccurate, and the workload can differ from the assumptions used during optimization. Tuning therefore involves understanding the decision and checking the observed work, rather than assuming that either Oracle or the developer always knows the best access path.
Optimizer statistics summarize information used during plan selection. They are different from execution statistics, which describe work observed while a statement runs. A stored table row count, for example, is not a continuously updated measurement of every row currently in the table.
| Input | What it helps explain |
|---|---|
| Table statistics | Row counts and blocks help estimate data volume and scan work. |
| Column statistics | Distinct values, null counts and other distribution information help estimate predicate selectivity. |
| Histograms | Where appropriate, they describe uneven value distributions more effectively than a uniform assumption. |
| Index statistics | Index structure and clustering factor help estimate traversal and associated table access. |
| System statistics and settings | Resource characteristics and the optimizer environment influence costing and available choices. |
Statistics do not give Oracle perfect knowledge of current data. They may be old, based on sampling, or insufficient to describe relationships between columns. Fresh table statistics can coexist with a poor estimate for a particular expression or combination of predicates.
Missing statistics do not switch Oracle 26ai to rule-based optimization. The optimizer can use other estimates and dynamic statistics where applicable. That capability does not remove the need to investigate whether the available information is suitable for an important workload.
Selectivity is the estimated fraction of rows matching a condition. Cardinality is an estimated number of output rows for an operation. If a hypothetical table contains 100,000 rows and a single filter is estimated to match 1 percent, a simplified estimate is 1,000 output rows. This arithmetic illustrates the relationship, not an observed database result.
Real queries can require more complex reasoning. Two conditions may be correlated rather than independent. A join can eliminate rows or produce several matches for each input row. Grouping and duplicate elimination also change output cardinality. Multiplying independent percentages is not a universal estimation formula.
A row-count error can affect later operations. If a nested loops join receives far more outer rows than expected, it may perform many more inner probes than anticipated. Incorrect estimates can also influence sorting, hashing and the amount of intermediate data passed through the plan. Look for where an estimate first becomes unrepresentative instead of blaming only the last expensive operation.
Cost is an internal estimate of resource work used to compare candidate plans. It is not a duration in seconds or a benchmark result. A lower cost for one query does not establish that it is faster than a different query. Costs from different optimizer goals are not a reliable cross-goal performance score either.
The optimization goal matters. Efficient completion of the entire statement and prompt delivery of initial rows can favor different strategies. Keep the goal and result-consumption requirements consistent when interpreting a comparison.
Consider a hypothetical EMPLOYEE table with 15,000 rows and an index on DEPARTMENT. These values are teaching assumptions, not captured output from an Oracle sample schema:
| Department | Employees | Share of rows |
|---|---|---|
| Accounting | 2,500 | One-sixth |
| Finance | 2,500 | One-sixth |
| Marketing | 10,000 | Two-thirds |
An assumption that department values are equally common would misrepresent this distribution. Appropriate column statistics and, where useful, a histogram can help Oracle distinguish common from less common values. This information can affect the estimated work of an index path compared with a scan.
However, requesting two-thirds of the rows does not prove that a full table scan is fastest. The selected columns matter: an index that supplies every required value can avoid table lookups, while another index may require many table block visits. Table size, row organization, clustering, storage characteristics and the optimizer goal also affect the comparison.
There is no universal five-percent threshold at which Oracle must prefer a scan. Likewise, an index is not automatically faster because it is present. The example explains why distribution matters; it does not prescribe an access path without measurements.
Modern Oracle costing considers CPU and I/O work, with memory and other relevant factors affecting the modeled operations. Avoid treating the cost-based optimizer as an I/O-only mechanism. A query can consume substantial CPU through predicate evaluation, expressions, joins, sorting or aggregation even when much of its data is already cached.
An index can reduce the number of rows examined but still cause scattered table access. A scan can inspect more rows while accessing blocks efficiently. The balance depends on the query and data, so neither operation is universally preferable.
A logical read accesses a database block through Oracle's processing mechanisms; it does not necessarily require reading that block from storage. A physical read retrieves data that must be obtained from storage. Do not interpret every index lookup or every scanned block as a separate physical disk read.
Memory available to work areas can affect sorting and hashing. Work that cannot remain in memory may require temporary-space activity. Concurrent workload, cache conditions and resource availability can therefore affect observed execution even when the displayed plan appears unchanged.
Equal elapsed times do not prove that extra CPU work was overlapped by I/O. A controlled investigation needs appropriate measurements and equivalent test conditions. Similarly, adding a WHERE predicate can change the answer, while COUNT(column) counts only non-null values. Comparing two superficially similar COUNT queries is not, by itself, a valid experiment isolating CPU cost.
Start with a read-only check of the relevant object. This example assumes an ordinary table named EMPLOYEE in the current user's schema. Substitute the actual owned table name in your practice environment:
SELECT table_name, num_rows, blocks, last_analyzed, stale_stats
FROM user_tab_statistics
WHERE table_name = 'EMPLOYEE'
AND partition_name IS NULL
AND subpartition_name IS NULL;
The partition filters focus the example on table-level information. No matching row means the assumed table is not represented by that name in this account's view. Check ownership and naming before treating an empty result as a statistics problem.
NUM_ROWS is the stored statistics value, not a live COUNT(*). LAST_ANALYZED records when statistics were analyzed, and STALE_STATS provides information about their staleness. Neither a recent timestamp nor a non-stale indication proves that every predicate estimate is accurate. Skew, correlated columns and bind-related behavior can still matter.
Review the query's assumptions alongside these values. Are the selected values representative? Has the table changed substantially since a load? Does a function or implicit conversion affect predicate interpretation? The goal is to identify an information problem, not simply to make a timestamp newer.
Oracle provides automatic optimizer statistics collection and the DBMS_STATS package. Automatic collection may already address an object's needs. Manual gathering can supplement it when justified by a particular change or workload, but a fixed weekly schedule is not a universal rule.
The following optional example gathers statistics for an owned practice table. It requires the table to exist, appropriate privileges, and a reason to change its statistics. It is a state-changing operation that can affect subsequent plan selection, not another inspection query:
BEGIN
DBMS_STATS.GATHER_TABLE_STATS(
ownname => USER,
tabname => 'EMPLOYEE',
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE
);
END;
/
AUTO_SAMPLE_SIZE lets Oracle determine an appropriate sample size. The slash is a SQL*Plus-style command for executing the PL/SQL block; another client may execute the block differently. Existing statistics preferences and locking policies must be considered. The example is not an instruction to override them or to gather production statistics without a reviewed need.
Evaluate statistics maintenance in the context of loads, data changes, workload importance and operational windows. Do not gather statistics after every small modification. Conversely, waiting for a routine schedule may be unsuitable after a major change if important statements now encounter unrepresentative estimates.
New statistics can change plans. Preserve a baseline and validate affected statements with representative inputs. A newer collection date does not guarantee that every statement improves, and collecting statistics repeatedly will not necessarily correct a problem caused by SQL semantics or an unsuitable predicate.
The EXPLAIN PLAN lesson explains how to display a proposed plan with DBMS_XPLAN.DISPLAY. That is useful estimated evidence, but a separately explained statement may differ from the application's executed cursor.
For runtime investigation, identify the intended SQL_ID and child cursor before using DBMS_XPLAN.DISPLAY_CURSOR. Use an account with the necessary view access. A statement can have multiple child cursors, and inspecting an unrelated recent cursor can produce a misleading diagnosis.
Runtime row-source statistics must have been collected for ALLSTATS LAST to display them. The option does not retrospectively measure a query. Use the controlled collection approach discussed in the SQL tuning tool set lesson, and confirm which execution the statistics represent.
Compare estimated and actual rows with attention to repeated starts. An operation invoked many times can report cumulative actual rows, while its estimate describes output per start. A large raw difference between two columns is not automatically an estimation failure. Interpret row counts alongside Starts and the parent-child relationships in the plan.
Then examine elapsed time and relevant resource work, such as buffers and physical reads. Match the test to the application's fetch behavior. Returning one page of results and consuming an entire export are different workloads. Client rendering and pauses between fetches can contribute to user-visible delay without being caused by the optimizer's access-path choice.
This approach makes a tuning claim reviewable. An index appearing in a plan, a lower printed cost, or a newer statistics timestamp is not enough by itself. The evidence should explain what work changed and whether that change helped the application's actual requirement.
For historical context, the retained Oracle Ranking Scheme page describes older access-path rankings. Read it as historical background, not a universal ordering of execution speed in Oracle 26ai.
The next lesson examines SQL hints. Hints can influence optimizer decisions, but their syntax, applicability and effect must be checked. They do not guarantee faster execution and should not replace an investigation of statistics, estimates and observed work.