The EXPLAIN PLAN statement in Oracle SQL provides detailed insights into how the database optimizer executes join operations, revealing the specific algorithms, access paths, and resource costs involved.
Understanding execution plans becomes crucial for identifying performance bottlenecks, optimizing join strategies, and ensuring efficient query execution in production environments.
Query performance tuning relies heavily on analyzing execution plans to make informed decisions about indexing, join order, and query structure modifications.
This article explores comprehensive implementation of Oracle SQL EXPLAIN PLAN specifically for join analysis, demonstrating how to interpret execution plans and optimize join performance.
What is EXPLAIN PLAN in Oracle SQL?
EXPLAIN PLAN is an Oracle SQL statement that generates and stores the execution plan that the Oracle optimizer would use to execute a specific query.
The execution plan reveals the step-by-step operations, join algorithms, and access methods that Oracle will employ to retrieve and combine data from multiple tables.
This diagnostic tool operates without actually executing the query, making it safe to analyze potentially expensive operations before running them in production.

The EXPLAIN PLAN output includes critical performance metrics such as estimated costs, cardinality estimates, and resource utilization projections.
Oracle stores the execution plan information in the PLAN_TABLE, which can be queried to extract detailed performance analysis data.
How Does EXPLAIN PLAN Work for Join Operations?
EXPLAIN PLAN analyzes the query structure and table statistics to determine the most efficient join algorithms and execution sequence.
The Oracle cost-based optimizer evaluates multiple execution strategies and selects the plan with the lowest estimated cost based on available statistics.
Join analysis includes determining the optimal join order, selecting appropriate join algorithms, and estimating the computational resources required for each operation.
The execution plan reveals how Oracle will access each table, whether through full table scans, index scans, or other access methods.
Understanding the join algorithms chosen by the optimizer helps identify opportunities for performance improvement through indexing or query restructuring.
What Are the Main Join Algorithms in Oracle?
Nested Loop Join represents the most straightforward join algorithm, processing each row from the outer table and searching for matching rows in the inner table.
Hash Join builds an in-memory hash table from the smaller table and probes it with rows from the larger table to find matches.
Sort-Merge Join sorts both tables by the join column and then merges the sorted results to identify matching rows.
Cartesian Join occurs when no join condition exists or when the optimizer cannot use other join methods effectively.
The choice of join algorithm significantly impacts query performance, with each method having specific advantages depending on data characteristics and available resources.
How to Set Up EXPLAIN PLAN for Join Analysis?
Setting up EXPLAIN PLAN requires ensuring that the PLAN_TABLE exists and is accessible for storing execution plan information.
-- Create the plan table if it doesn't exist @$ORACLE_HOME/rdbms/admin/utlxplan.sql
The basic syntax for EXPLAIN PLAN includes the statement_id parameter for organizing multiple execution plans.
EXPLAIN PLAN SET STATEMENT_ID = 'join_analysis_1' FOR your_sql_query;
After generating the execution plan, you can query the PLAN_TABLE to retrieve and analyze the results.
Oracle also provides the DBMS_XPLAN package for formatted output that makes execution plans easier to read and interpret.
How to Create Sample Tables for Join Analysis?
Let's create comprehensive sample tables to demonstrate practical EXPLAIN PLAN implementations for join analysis.
Data Preparation
-- Create customers table
CREATE TABLE expln_customers (
customer_id NUMBER PRIMARY KEY,
customer_name VARCHAR2(100),
email VARCHAR2(100),
registration_date DATE,
city VARCHAR2(50),
country VARCHAR2(50)
);
-- Create orders table
CREATE TABLE expln_orders (
order_id NUMBER PRIMARY KEY,
customer_id NUMBER,
order_date DATE,
total_amount NUMBER(10,2),
order_status VARCHAR2(20),
sales_rep_id NUMBER
);
-- Create order_details table
CREATE TABLE expln_order_details (
detail_id NUMBER PRIMARY KEY,
order_id NUMBER,
product_id NUMBER,
quantity NUMBER,
unit_price NUMBER(8,2),
line_total NUMBER(10,2)
);
-- Create products table
CREATE TABLE expln_products (
product_id NUMBER PRIMARY KEY,
product_name VARCHAR2(100),
category_id NUMBER,
unit_price NUMBER(8,2),
stock_quantity NUMBER
);
-- Insert sample data with sufficient volume for meaningful analysis
INSERT INTO expln_customers
SELECT LEVEL as customer_id,
'Customer ' || LEVEL as customer_name,
'customer' || LEVEL || '@email.com' as email,
DATE '2024-01-01' + MOD(LEVEL, 365) as registration_date,
CASE MOD(LEVEL, 5)
WHEN 0 THEN 'New York'
WHEN 1 THEN 'Chicago'
WHEN 2 THEN 'Los Angeles'
WHEN 3 THEN 'Houston'
ELSE 'Phoenix'
END as city,
'USA' as country
FROM DUAL
CONNECT BY LEVEL <= 10000;
INSERT INTO expln_products
SELECT LEVEL as product_id,
'Product ' || LEVEL as product_name,
MOD(LEVEL, 50) + 1 as category_id,
ROUND(DBMS_RANDOM.VALUE(10, 500), 2) as unit_price,
ROUND(DBMS_RANDOM.VALUE(0, 1000)) as stock_quantity
FROM DUAL
CONNECT BY LEVEL <= 5000;
INSERT INTO expln_orders
SELECT LEVEL as order_id,
MOD(LEVEL, 10000) + 1 as customer_id,
DATE '2024-01-01' + MOD(LEVEL, 365) as order_date,
ROUND(DBMS_RANDOM.VALUE(50, 2000), 2) as total_amount,
CASE MOD(LEVEL, 4)
WHEN 0 THEN 'Completed'
WHEN 1 THEN 'Processing'
WHEN 2 THEN 'Shipped'
ELSE 'Cancelled'
END as order_status,
MOD(LEVEL, 100) + 1 as sales_rep_id
FROM DUAL
CONNECT BY LEVEL <= 25000;
INSERT INTO expln_order_details
SELECT LEVEL as detail_id,
MOD(LEVEL, 25000) + 1 as order_id,
MOD(LEVEL, 5000) + 1 as product_id,
ROUND(DBMS_RANDOM.VALUE(1, 10)) as quantity,
ROUND(DBMS_RANDOM.VALUE(10, 500), 2) as unit_price,
ROUND(DBMS_RANDOM.VALUE(10, 5000), 2) as line_total
FROM DUAL
CONNECT BY LEVEL <= 75000;
-- Create indexes for different join scenarios
CREATE INDEX idx_expln_orders_customer_id ON expln_orders(customer_id);
CREATE INDEX idx_expln_orders_date ON expln_orders(order_date);
CREATE INDEX idx_expln_order_details_order_id ON expln_order_details(order_id);
CREATE INDEX idx_expln_order_details_product_id ON expln_order_details(product_id);
CREATE INDEX idx_expln_products_category_id ON expln_products(category_id);
-- Gather table statistics for accurate cost estimation
EXEC DBMS_STATS.GATHER_TABLE_STATS('USER', 'EXPLN_CUSTOMERS');
EXEC DBMS_STATS.GATHER_TABLE_STATS('USER', 'EXPLN_ORDERS');
EXEC DBMS_STATS.GATHER_TABLE_STATS('USER', 'EXPLN_ORDER_DETAILS');
EXEC DBMS_STATS.GATHER_TABLE_STATS('USER', 'EXPLN_PRODUCTS');
How to Analyze Basic Join Operations with EXPLAIN PLAN?
Basic join analysis starts with simple two-table joins to understand the fundamental execution plan components and join algorithms.
Nested Loop Join Analysis
This example demonstrates analyzing a nested loop join between customers and orders.
-- Generate execution plan for nested loop join scenario
EXPLAIN PLAN
SET STATEMENT_ID = 'nested_loop_join'
FOR
SELECT c.customer_id, c.customer_name, o.order_id, o.total_amount
FROM expln_customers c
JOIN expln_orders o ON c.customer_id = o.customer_id
WHERE c.customer_id BETWEEN 1 AND 100;
-- Query the execution plan
SELECT operation, options, object_name, cost, cardinality, bytes
FROM plan_table
WHERE statement_id = 'nested_loop_join'
ORDER BY id;
Query Result
OPERATION OPTIONS OBJECT_NAME COST CARDINALITY BYTES
---------------------- ------------------------ ------------------------------ ---- ----------- -----
SELECT STATEMENT 78 101 10504
HASH JOIN 78 101 10504
NESTED LOOPS 78 101 10504
NESTED LOOPS
STATISTICS COLLECTOR
TABLE ACCESS BY INDEX ROWID BATCHED EXPLN_CUSTOMERS 10 100 6500
INDEX RANGE SCAN SYS_C002187737 2 100
INDEX RANGE SCAN IDX_EXPLN_ORDERS_CUSTOMER_ID
TABLE ACCESS BY INDEX ROWID EXPLN_ORDERS 68 1 39
TABLE ACCESS FULL EXPLN_ORDERS 68 299 11661
The execution plan shows Oracle chose a nested loop join with index access for both tables, indicating efficient selective access patterns.
Hash Join Analysis
This example demonstrates a hash join scenario with larger result sets.
-- Generate execution plan for hash join scenario
EXPLAIN PLAN
SET STATEMENT_ID = 'hash_join'
FOR
SELECT c.city, COUNT(o.order_id) as order_count, SUM(o.total_amount) as total_sales
FROM expln_customers c
JOIN expln_orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= DATE '2024-06-01'
GROUP BY c.city;
-- Query the execution plan using DBMS_XPLAN for better formatting
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE', 'hash_join', 'BASIC +COST +BYTES'));
Query Result
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
Plan hash value: 2396269074
--------------------------------------------------------------------
| Id | Operation | Name | Bytes | Cost (%CPU)|
--------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 942K| 97 (3)|
| 1 | HASH GROUP BY | | 942K| 97 (3)|
| 2 | HASH JOIN | | 942K| 95 (0)|
| 3 | TABLE ACCESS FULL| EXPLN_CUSTOMERS | 395K| 27 (0)|
| 4 | TABLE ACCESS FULL| EXPLN_ORDERS | 439K| 68 (0)|
--------------------------------------------------------------------
The execution plan reveals Oracle selected a hash join with full table scans, appropriate for processing larger datasets with aggregation.
What Are Advanced Join Analysis Techniques?
Advanced join analysis involves examining complex multi-table joins, understanding join order optimization, and identifying performance improvement opportunities.
Multi-Table Join Analysis
This example demonstrates analyzing a four-table join with different join algorithms.
-- Generate execution plan for complex multi-table join
EXPLAIN PLAN
SET STATEMENT_ID = 'multi_table_join'
FOR
SELECT c.customer_name,
o.order_id,
p.product_name,
od.quantity,
od.line_total,
o.order_date
FROM expln_customers c
JOIN expln_orders o ON c.customer_id = o.customer_id
JOIN expln_order_details od ON o.order_id = od.order_id
JOIN expln_products p ON od.product_id = p.product_id
WHERE c.city = 'New York'
AND o.order_date >= DATE '2024-01-01'
AND p.unit_price > 100
ORDER BY o.order_date DESC, od.line_total DESC;
-- Analyze the complex execution plan
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY('PLAN_TABLE', 'multi_table_join', 'ALL'));
Query Result
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------
Plan hash value: 4036242163
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10496 | 2634K| | 792 (1)| 00:00:01 |
| 1 | SORT ORDER BY | | 10496 | 2634K| 2808K| 792 (1)| 00:00:01 |
|* 2 | HASH JOIN | | 10496 | 2634K| | 208 (1)| 00:00:01 |
|* 3 | TABLE ACCESS FULL | EXPLN_PRODUCTS | 4042 | 307K| | 9 (0)| 00:00:01 |
|* 4 | HASH JOIN | | 13124 | 2294K| | 198 (1)| 00:00:01 |
|* 5 | HASH JOIN | | 5783 | 717K| | 95 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| EXPLN_CUSTOMERS | 2021 | 181K| | 27 (0)| 00:00:01 |
|* 7 | TABLE ACCESS FULL| EXPLN_ORDERS | 22820 | 779K| | 68 (0)| 00:00:01 |
| 8 | TABLE ACCESS FULL | EXPLN_ORDER_DETAILS | 62882 | 3193K| | 103 (1)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
The execution plan shows Oracle's join order optimization, starting with the most selective conditions and using appropriate join algorithms for each step.
How to Interpret Cost and Cardinality in Execution Plans?
Cost represents Oracle's estimation of the computational resources required to execute each operation in the execution plan.
Cardinality indicates the estimated number of rows that each operation will process or return, helping identify potential performance bottlenecks.
The total query cost appears in the SELECT STATEMENT operation and represents the cumulative cost of all child operations.
High cardinality estimates in early join operations often indicate inefficient access paths or missing indexes that could improve performance.
Comparing estimated versus actual cardinalities helps identify statistics issues or optimizer limitations that might require manual tuning.
Cost Analysis Example
-- Generate execution plan with detailed cost information
EXPLAIN PLAN
SET STATEMENT_ID = 'cost_analysis'
FOR
SELECT /*+ GATHER_PLAN_STATISTICS */
c.customer_name,
COUNT(o.order_id) as order_count,
AVG(o.total_amount) as avg_order_value
FROM expln_customers c
LEFT JOIN expln_orders o ON c.customer_id = o.customer_id
WHERE c.registration_date >= DATE '2024-01-01'
GROUP BY c.customer_id, c.customer_name
HAVING COUNT(o.order_id) > 2;
-- Analyze cost breakdown
SELECT id, operation, options, object_name, cost, cardinality, bytes,
ROUND(cost/SUM(cost) OVER () * 100, 2) as cost_percentage
FROM plan_table
WHERE statement_id = 'cost_analysis'
AND cost > 0
ORDER BY cost DESC;
Query Result
ID OPERATION OPTIONS OBJECT_NAME COST CARDINALITY BYTES COST_PERCENTAGE
-- ------------------ ------------------------ ----------------- ---- ----------- ------- ---------------
0 SELECT STATEMENT 97 24967 2821271 17.67
2 HASH GROUP BY 97 24967 2821271 17.67
3 HASH JOIN OUTER 96 24967 2821271 17.49
4 NESTED LOOPS OUTER 96 24967 2821271 17.49
7 TABLE ACCESS BY INDEX ROWID BATCHED EXPLN_ORDERS 68 2 78 12.39
9 TABLE ACCESS FULL EXPLN_ORDERS 68 22820 889980 12.39
6 TABLE ACCESS FULL EXPLN_CUSTOMERS 27 10120 748880 4.92
The cost analysis reveals that the orders table access represents the highest cost component, suggesting potential optimization opportunities.
What Are Join Algorithm Selection Factors?
Oracle's cost-based optimizer considers multiple factors when selecting join algorithms, including table sizes, available indexes, and selectivity of join conditions.
Nested loop joins perform best when one table is small or highly selective, making them ideal for OLTP queries with specific key lookups.
Hash joins excel with medium to large datasets where one table can fit in memory, providing efficient processing for analytical workloads.
Sort-merge joins work well when both tables are large and already sorted, or when memory constraints limit hash join effectiveness.
Understanding these selection criteria helps database developers write queries that encourage optimal join algorithm choices.
Join Algorithm Comparison
-- Force different join algorithms to compare performance
EXPLAIN PLAN
SET STATEMENT_ID = 'nested_loop_hint'
FOR
SELECT /*+ USE_NL(c o) */
c.customer_name, o.order_date, o.total_amount
FROM expln_customers c
JOIN expln_orders o ON c.customer_id = o.customer_id
WHERE c.city = 'Chicago';
EXPLAIN PLAN
SET STATEMENT_ID = 'hash_join_hint'
FOR
SELECT /*+ USE_HASH(c o) */
c.customer_name, o.order_date, o.total_amount
FROM expln_customers c
JOIN expln_orders o ON c.customer_id = o.customer_id
WHERE c.city = 'Chicago';
-- Compare costs of different join algorithms
SELECT statement_id,
operation,
options,
cost,
cardinality
FROM plan_table
WHERE statement_id IN ('nested_loop_hint', 'hash_join_hint')
AND operation IN ('NESTED LOOPS', 'HASH JOIN')
ORDER BY statement_id, cost;
Query Result
STATEMENT_ID OPERATION OPTIONS COST CARDINALITY
------------------ -------------- ------- ---- -----------
hash_join_hint HASH JOIN 95 5783
nested_loop_hint NESTED LOOPS 8421 5783
nested_loop_hint NESTED LOOPS 8421 6063
The comparison shows the hash join algorithm provides better cost efficiency for this particular query and data distribution.
How to Identify Join Performance Problems?
High-cost operations in execution plans often indicate performance bottlenecks that require optimization attention.
Full table scans on large tables without appropriate filtering conditions suggest missing indexes or inefficient query design.
Cartesian products appearing in execution plans indicate missing or incorrect join conditions that will cause exponential performance degradation.
Dramatic differences between estimated and actual cardinalities point to outdated statistics or complex data distributions that confuse the optimizer.
Sequential operations that could benefit from parallelization represent opportunities for performance improvement in analytical workloads.
Performance Problem Analysis
-- Example of problematic query for analysis
EXPLAIN PLAN
SET STATEMENT_ID = 'performance_problem'
FOR
SELECT c.customer_name,
o.order_date,
p.product_name,
od.quantity
FROM expln_customers c,
expln_orders o,
expln_order_details od,
expln_products p
WHERE c.city LIKE '%York%'
AND o.total_amount > 500
AND p.unit_price > 200;
-- Identify performance issues in the execution plan
SELECT operation, options, object_name, cost, cardinality,
CASE
WHEN operation = 'CARTESIAN' THEN 'CRITICAL: Missing join condition'
WHEN operation LIKE '%FULL%' AND cost > 1000 THEN 'WARNING: Expensive full scan'
WHEN cardinality > 100000 THEN 'REVIEW: High cardinality'
ELSE 'OK'
END as performance_assessment
FROM plan_table
WHERE statement_id = 'performance_problem'
ORDER BY cost DESC;
Query Result
OPERATION OPTIONS OBJECT_NAME COST CARDINALITY PERFORMANCE_ASSESSMENT
------------------ ----------- --------------------- -------------- ---------------- --------------------------
SELECT STATEMENT 10770584305929 6720535256048894 REVIEW: High cardinality
MERGE JOIN CARTESIAN 10770584305929 6720535256048894 REVIEW: High cardinality
BUFFER SORT 10770584305921 62882 OK
MERGE JOIN CARTESIAN 270757697 106874694537 REVIEW: High cardinality
BUFFER SORT 270757631 3016 OK
MERGE JOIN CARTESIAN 134138 35435907 REVIEW: High cardinality
BUFFER SORT 134111 17537 OK
TABLE ACCESS FULL EXPLN_ORDER_DETAILS 101 62882 OK
TABLE ACCESS FULL EXPLN_ORDERS 66 17537 OK
TABLE ACCESS FULL EXPLN_CUSTOMERS 27 2021 OK
TABLE ACCESS FULL EXPLN_PRODUCTS 8 3016 OK
The analysis clearly identifies the cartesian product problem caused by missing join conditions, resulting in astronomical costs and cardinalities.
What Are Best Practices for Join Optimization?
Always ensure proper join conditions exist between all related tables to prevent cartesian products and ensure accurate results.
Create appropriate indexes on join columns and frequently filtered attributes to enable efficient access paths and join algorithms.
Keep table and index statistics current through regular gathering to ensure the optimizer makes informed decisions based on accurate data distributions.
Use selective WHERE clauses early in the query to reduce the working set size and improve join efficiency.
Consider query restructuring, including subqueries or common table expressions, when complex joins produce inefficient execution plans.
Optimization Best Practices Example
-- Optimized version of the problematic query
EXPLAIN PLAN
SET STATEMENT_ID = 'optimized_query'
FOR
SELECT c.customer_name,
o.order_date,
p.product_name,
od.quantity
FROM expln_customers c
JOIN expln_orders o ON c.customer_id = o.customer_id
JOIN expln_order_details od ON o.order_id = od.order_id
JOIN expln_products p ON od.product_id = p.product_id
WHERE c.city LIKE '%York%'
AND o.total_amount > 500
AND p.unit_price > 200;
-- Compare optimized vs problematic execution plans
SELECT 'Optimized' as query_type, SUM(cost) as total_cost, MAX(cardinality) as max_cardinality
FROM plan_table
WHERE statement_id = 'optimized_query'
UNION ALL
SELECT 'Problematic' as query_type, SUM(cost) as total_cost, MAX(cardinality) as max_cardinality
FROM plan_table
WHERE statement_id = 'performance_problem';
Query Result
QUERY_TYPE TOTAL_COST MAX_CARDINALITY
------------- ---------- ---------------
Optimized 1077 62882
Problematic
The optimization demonstrates dramatic improvement through proper join conditions and selective filtering.
Conclusion
Oracle SQL EXPLAIN PLAN provides essential capabilities for analyzing and optimizing join operations by revealing the specific algorithms, costs, and execution strategies chosen by the optimizer.
Understanding execution plan components, including join algorithms, cost estimates, and cardinality projections, enables database professionals to identify performance bottlenecks and optimization opportunities.
The ability to interpret and compare different execution plans empowers developers to make informed decisions about query structure, indexing strategies, and performance tuning approaches.
