Subquery Best Practices FAQS

1. What Are Oracle Subqueries?

A subquery is a query nested inside another query. Subqueries can be:

  • Single-row: returns one row used with =, <, >
  • Multi-row: returns multiple rows used with IN, ANY, ALL
  • Scalar: returns one value used as expression in SELECT/WHERE
  • Correlated: references outer query columns executed per row
  • Non-Correlated: independent executed once
  • Inline View: used in FROM clause as derived table

2. Why Follow Best Practices?

  • Avoid performance issues (especially with correlated subqueries)
  • Ensure correct results (avoid NULL pitfalls, ORA-01427 errors)
  • Improve readability and maintainability
  • Make queries optimizer-friendly for faster execution

3. Best Practice 1: Prefer Non-Correlated Subqueries

  • Non-correlated subqueries are executed once faster
  • Correlated subqueries execute per row slower for large datasets

Example:

SELECT e.employee_id

FROM employees e

WHERE salary > (

    SELECT AVG(salary)

    FROM employees

    WHERE department_id = e.department_id

);

 

SELECT employee_id

FROM (

    SELECT employee_id, salary,

           AVG(salary) OVER (PARTITION BY department_id) dept_avg

    FROM employees

)

WHERE salary > dept_avg;

4. Best Practice 2: Use Aggregates to Ensure Single-Row Subquery

  • Single-row subqueries must return exactly one row
  • Use aggregate functions like AVG(), MAX(), MIN(), COUNT() to guarantee a single value

Example:

SELECT employee_id

FROM employees

WHERE salary > (SELECT AVG(salary) FROM employees);

5. Best Practice 3: Prefer EXISTS Instead of IN for Correlated Subqueries

  • EXISTS is optimized for checking existence
  • Stops processing once condition is met
  • Safer when dealing with NULLs

Example:

SELECT e.employee_id

FROM employees e

WHERE EXISTS (

    SELECT 1

    FROM job_history j

    WHERE j.employee_id = e.employee_id

);

 

SELECT e.employee_id

FROM employees e

WHERE e.employee_id IN (

    SELECT employee_id

    FROM job_history

);

6. Best Practice 4: Avoid NOT IN With NULLs

  • NOT IN fails if subquery returns NULL
  • Use NOT EXISTS instead

Example:

SELECT e.employee_id

FROM employees e

WHERE NOT EXISTS (

    SELECT 1

    FROM departments d

    WHERE e.department_id = d.department_id

);

7. Best Practice 5: Use Inline Views or WITH Clause for Complex Aggregation

  • Simplifies query
  • Optimizer can merge inline views
  • Avoids repeated computation

Inline View:

SELECT e.employee_id, d.avg_sal

FROM employees e

JOIN (

    SELECT department_id, AVG(salary) AS avg_sal

    FROM employees

    GROUP BY department_id

) d

ON e.department_id = d.department_id

WHERE e.salary > d.avg_sal;

WITH Clause (CTE):

WITH dept_avg AS (

    SELECT department_id, AVG(salary) AS avg_sal

    FROM employees

    GROUP BY department_id

)

SELECT e.employee_id, e.salary

FROM employees e

JOIN dept_avg d

ON e.department_id = d.department_id

WHERE e.salary > d.avg_sal;

8. Best Practice 6: Index Correlated Columns

  • Correlated subqueries reference outer columns repeated scans
  • Indexing improves performance

Example:

CREATE INDEX idx_emp_dept ON employees(department_id);

9. Best Practice 7: Use Analytic Functions Instead of Correlated Subqueries

  • Analytic functions calculate per-group results once per partition
  • Avoid repeated inner query execution

Example:

SELECT employee_id, salary,

       AVG(salary) OVER (PARTITION BY department_id) dept_avg

FROM employees

WHERE salary > dept_avg;

10. Best Practice 8: Limit Rows Early

  • Use WHERE clauses in subqueries to reduce unnecessary processing

Example:

SELECT employee_id

FROM employees

WHERE department_id IN (

    SELECT department_id

    FROM departments

    WHERE location_id = 1700

);

11. Best Practice 9: Avoid Deeply Nested Subqueries

  • Excessive nesting hard to read and harder for optimizer
  • Flatten queries using JOINs, inline views, or CTEs

12. Best Practice 10: Check Execution Plan

  • Use EXPLAIN PLAN to see subquery execution
  • Check for full table scans, nested loops on large datasets, and unnesting opportunities

Example:

EXPLAIN PLAN FOR

SELECT e.employee_id

FROM employees e

WHERE salary > (

    SELECT AVG(salary)

    FROM employees

);

SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY());

13. Summary Table of Best Practices

Best Practice

Description

Prefer Non-Correlated Subquery

Executes once, faster

Use Aggregates for Single-Row

Prevent ORA-01427

Prefer EXISTS over IN

Optimized for correlated checks

Avoid NOT IN with NULLs

Use NOT EXISTS

Inline Views / CTEs

Simplifies complex queries

Index Correlated Columns

Improves performance

Use Analytic Functions

Efficient per-group computation

Limit Rows Early

Reduces unnecessary processing

Avoid Deep Nesting

Maintain readability & optimizer efficiency

Check Execution Plan

Validate optimizer choices

14. Short Interview Answer

“Oracle subquery best practices include using non-correlated subqueries when possible, preferring EXISTS over IN for correlated queries, using aggregates to guarantee single-row results, indexing correlated columns, limiting rows early, using inline views or CTEs for complex aggregations, and leveraging analytic functions to replace repeated correlated subqueries. Always check the execution plan to ensure optimal performance.”

 

Subquery Performance FAQS

1. What Is a Subquery in Oracle?

A subquery is a query nested inside another SQL statement. Subqueries can be:

·        Single-row (returns one value)

·        Multi-row (returns multiple values)

·        Scalar (returns a single value used in expressions)

·        Correlated (references outer query)

·        Non-Correlated (independent of outer query)

·        Inline View (used in FROM clause as a derived table)

Performance depends on type, execution frequency, and indexing.

2. How Does Oracle Execute Subqueries?

Non-Correlated Subquery

·        Executed once; result reused by outer query

Example:

SELECT employee_id
FROM employees
WHERE salary > (SELECT AVG(salary) FROM employees);

Correlated Subquery

·        Executed once per outer row; can be expensive for large tables

Example:

SELECT e.employee_id
FROM employees e
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
    WHERE department_id = e.department_id
);

3. Factors Affecting Subquery Performance

1.    Subquery Type: Non-correlated usually fast; correlated can be slow

2.    Indexes: Indexed columns improve performance

3.    Aggregation: Aggregates like AVG(), MAX(), COUNT() are optimized

4.    Number of Rows Returned: Single-row safe with =, >, <; multi-row requires IN, ANY, ALL

5.    Nested Subqueries: Deep nesting may slow queries; optimizer may merge

6.    Analytic Functions: Can replace correlated subqueries efficiently

4. Performance Tips: Correlated Subquery

·        Use EXISTS instead of IN

·        Add indexes on correlated columns

·        Consider JOIN or analytic functions

·        Limit outer query rows early

5. Performance Tips: Non-Correlated Subquery

·        Executed once; usually acceptable performance

·        Can use JOIN or inline view for optimization

Example:

SELECT e.employee_id, e.salary
FROM employees e
JOIN (
    SELECT department_id, AVG(salary) AS avg_sal
    FROM employees
    GROUP BY department_id
) d
ON e.department_id = d.department_id
WHERE e.salary > d.avg_sal;

6. Inline View Performance

·        Optimizer may merge inline view with outer query

·        Avoid excessive nesting

·        Prefer analytic functions for repeated aggregation

7. EXISTS vs IN Performance

Feature

EXISTS

IN

Checks existence

Yes

Compares values

Optimized for large datasets

Often better

May be slower

Handles NULLs

Yes

NOT IN may fail if NULL present

Execution

Stops at first match

Compares against all

8. NOT IN Considerations

·        NOT IN can return no rows if subquery contains NULL

·        Use NOT EXISTS as safer alternative:

SELECT e.employee_id
FROM employees e
WHERE NOT EXISTS (
    SELECT 1
    FROM departments d
    WHERE e.department_id = d.department_id
);

9. Using Aggregate Functions

·        Aggregates reduce result size faster outer query

Example:

SELECT employee_id
FROM employees
WHERE salary > (SELECT MAX(salary) FROM employees);

10. Subquery Unnesting

·        Optimizer may transform subqueries into JOINs internally

·        Correlated Non-correlated using JOIN or analytic function

Example:

-- Correlated subquery
SELECT e.employee_id
FROM employees e
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
    WHERE department_id = e.department_id
);
 
-- Optimized using analytic function
SELECT employee_id
FROM (
    SELECT employee_id,
           AVG(salary) OVER (PARTITION BY department_id) dept_avg,
           salary
    FROM employees
)
WHERE salary > dept_avg;

11. Common Mistakes That Hurt Performance

·        Correlated subquery without index

·        Using NOT IN with potential NULLs

·        Returning more rows than needed

·        Deeply nested inline views

·        Assuming subquery always slower than JOIN

12. Best Practices for Subquery Performance

·        Use indexes on subquery columns

·        Use analytic functions for per-group calculations

·        Prefer EXISTS over IN for large correlated datasets

·        Avoid NOT IN with NULLs

·        Use inline views for aggregation before filtering

·        Check execution plan with EXPLAIN PLAN

·        Limit result set early

13. Real-World Examples

Example 1: Correlated Subquery (Slower)

SELECT e.employee_id
FROM employees e
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
    WHERE department_id = e.department_id
);

Example 2: Using Analytic Function (Faster)

SELECT employee_id
FROM (
    SELECT employee_id, salary,
           AVG(salary) OVER (PARTITION BY department_id) dept_avg
    FROM employees
)
WHERE salary > dept_avg;

14. Short Interview Answer

“Subquery performance in Oracle depends on type, correlation, indexing, and result size. Non-correlated subqueries execute once and are usually fast. Correlated subqueries execute per row and may be slow. Performance can be improved using indexes, EXISTS, analytic functions, inline views, or JOINs. Avoid NOT IN with NULLs and always check the execution plan.”