⚡ Module 15: Query Optimization & Execution Plans

EXPLAIN PLAN, Cost-Based Optimizer, Optimizer Hints

PLAN
Execution
CBO
Optimizer
HINT
Directives
ANALYZE
Statistics

📊 EXPLAIN PLAN Basics

Capturing Execution Plans

-- Create plan table (if not exists)
CREATE TABLE plan_table (
  statement_id VARCHAR2(30),
  plan_id NUMBER,
  timestamp DATE,
  remarks VARCHAR2(4000),
  operation VARCHAR2(30),
  options VARCHAR2(255),
  object_node VARCHAR2(128),
  object_owner VARCHAR2(30),
  object_name VARCHAR2(30),
  object_alias VARCHAR2(65),
  object_instance NUMBER,
  object_type VARCHAR2(30),
  optimizer VARCHAR2(255),
  search_columns NUMBER,
  id NUMBER,
  parent_id NUMBER,
  depth NUMBER,
  position NUMBER,
  cost NUMBER,
  cardinality NUMBER,
  bytes NUMBER,
  other_tag VARCHAR2(255),
  partition_start VARCHAR2(255),
  partition_stop VARCHAR2(255),
  partition_id NUMBER,
  other LONG,
  distribution VARCHAR2(30),
  cpu_cost NUMBER,
  io_cost NUMBER,
  temp_space NUMBER,
  access_predicates VARCHAR2(4000),
  filter_predicates VARCHAR2(4000),
  projection VARCHAR2(4000),
  time NUMBER,
  qblock_name VARCHAR2(30)
);

-- Explain a query
EXPLAIN PLAN
SET statement_id = 'query_001'
FOR
SELECT * FROM employees
WHERE salary > 50000;

-- View the plan
SELECT *
FROM table(dbms_xplan.display('plan_table', 'query_001'));

🎯 Understanding Execution Plans

Common Execution Plan Operations

Operation Description Use Case
FULL TABLE SCAN Read entire table sequentially Small tables, no index available
INDEX RANGE SCAN Use index to find range of rows Equality/range predicates
INDEX UNIQUE SCAN Use unique index for exact match Primary key lookup
HASH JOIN Hash match two tables Join large datasets
NESTED LOOP Loop through driving table Join small results
SORT Sort result set ORDER BY, GROUP BY
FILTER Apply WHERE clause Post-predicate filtering

💡 Optimizer Hints

Using Hints in SQL

-- Force full table scan
SELECT /*+ FULL(e) */ *
FROM employees e
WHERE salary > 50000;

-- Force index usage
SELECT /*+ INDEX(e idx_salary) */ *
FROM employees e
WHERE salary > 50000;

-- Force join method
SELECT /*+ HASH_JOIN(e d) */ e.*, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

-- Parallel execution
SELECT /*+ PARALLEL(4) */ count(*)
FROM large_table;

-- No merge (avoid subquery merge)
SELECT /*+ NO_MERGE(sq) */ *
FROM (
  SELECT emp_id, salary
  FROM employees
  WHERE salary > 50000
) sq;

-- Force driving table
SELECT /*+ LEADING(d) */ e.*, d.dept_name
FROM employees e
JOIN departments d ON e.dept_id = d.dept_id;

📈 Statistics & Analysis

Gathering Table Statistics

-- Gather table statistics
BEGIN
  DBMS_STATS.GATHER_TABLE_STATS(
    ownname => 'SCOTT',
    tabname => 'EMPLOYEES',
    estimate_percent => 10,
    method_opt => 'FOR ALL COLUMNS SIZE AUTO'
  );
END;
/

-- Gather schema statistics
BEGIN
  DBMS_STATS.GATHER_SCHEMA_STATS(
    ownname => 'SCOTT',
    options => 'GATHER',
    estimate_percent => 10,
    degree => 4
  );
END;
/

-- View table statistics
SELECT table_name, num_rows, blocks, avg_row_len
FROM user_tables
WHERE table_name = 'EMPLOYEES';

-- View column statistics
SELECT column_name, num_distinct, low_value, high_value
FROM user_tab_columns
WHERE table_name = 'EMPLOYEES';

🔧 Optimization Best Practices

Query Performance Tips

-- Efficient join with small result
SELECT e.emp_id, e.name, d.dept_name
FROM departments d
INNER JOIN employees e ON d.dept_id = e.dept_id
WHERE d.dept_id = 10;

-- Use EXISTS instead of IN for correlated subqueries
SELECT emp_id, name
FROM employees e
WHERE EXISTS (
  SELECT 1
  FROM assignments
  WHERE emp_id = e.emp_id
);

-- Avoid function calls in WHERE (prevents index usage)
-- BAD: WHERE UPPER(name) = 'JOHN'
-- GOOD:
SELECT emp_id, name
FROM employees
WHERE name = 'John';

-- Use UNION instead of OR for index efficiency
SELECT emp_id, name
FROM employees
WHERE dept_id = 10
UNION
SELECT emp_id, name
FROM employees
WHERE salary > 100000;
💡 Optimization Guidelines: Always gather statistics before tuning. Analyze plans with DBMS_XPLAN. Use hints judiciously. Avoid function calls on indexed columns. Use indexes on WHERE/JOIN columns. Monitor query execution time. Consider partitioning large tables. Archive old data. Use materialized views for complex aggregations.
✓ Learning Checklist - Module 15