Common Table Expression (CTE) in PLSQL | Part 1

Pranav Bakare - Sep 26 - - Dev Community

In Oracle SQL, a Common Table Expression (CTE) is a powerful tool that allows you to define a temporary result set that can be referenced within a SELECT, INSERT, UPDATE, or DELETE statement. CTEs can help simplify complex queries, improve readability, and enable recursive queries.

Part2

Basic Syntax of a CTE

WITH cte_name AS (
    -- CTE query
    SELECT column1, column2
    FROM table_name
    WHERE condition
)
SELECT *
FROM cte_name;
Enter fullscreen mode Exit fullscreen mode

Key Features of CTEs

  1. Temporary Result Set: CTEs exist only during the execution of the query.
  2. Readability: They can make complex SQL queries easier to read and understand.
  3. Recursion: CTEs can be recursive, allowing you to work with hierarchical data.

Example of Using CTE in Oracle

Step 1: Create a Sample Table

Let's create an Employees table.

CREATE TABLE Employees (
    id NUMBER PRIMARY KEY,
    name VARCHAR2(100),
    salary NUMBER,
    department_id NUMBER
);
Enter fullscreen mode Exit fullscreen mode

Step 2: Insert Sample Data

Insert some sample data into the Employees table.

INSERT INTO Employees (id, name, salary, department_id) 
VALUES (1, 'Alice', 70000, 1);
INSERT INTO Employees (id, name, salary, department_id) 
VALUES (2, 'Bob', 60000, 1);
INSERT INTO Employees (id, name, salary, department_id) 
VALUES (3, 'Charlie', 80000, 2);
INSERT INTO Employees (id, name, salary, department_id) 
VALUES (4, 'David', 50000, 2);
INSERT INTO Employees (id, name, salary, department_id) 
VALUES (5, 'Eve', 90000, 3);
Enter fullscreen mode Exit fullscreen mode

Step 3: Use a CTE to Calculate Average Salary by Department

Here’s an example of how to use a CTE to calculate the average salary of employees by department.

WITH AverageSalary AS (
    SELECT 
        department_id,
        AVG(salary) AS avg_salary
    FROM 
        Employees
    GROUP BY 
        department_id
)
SELECT 
    department_id,
    avg_salary
FROM 
    AverageSalary
WHERE 
    avg_salary > 60000;
Enter fullscreen mode Exit fullscreen mode

Explanation of the CTE Query

1. CTE Definition:

  • The CTE is defined using the WITH clause.
  • It calculates the average salary for each department by grouping the results based on department_id.

2. Main Query:

  • The main query selects department_id and avg_salary from the CTE
  • It filters the results to show only those departments where the average salary is greater than 60,000.

Expected Output

When you run the CTE query, the output should look something like this:

DEPARTMENT_ID   AVG_SALARY
1               65000
2               65000
3               90000

Enter fullscreen mode Exit fullscreen mode

Common Table Expressions (CTEs) are a powerful feature in SQL that provide a temporary result set that you can reference within a SELECT, INSERT, UPDATE, or DELETE statement. CTEs make it easier to read and maintain complex queries.


Certainly! Let’s go into detail for each of the topics mentioned in the interview questions, providing a clearer understanding of Common Table Expressions (CTEs) in PL/SQL or SQL in general.

1. What is a Common Table Expression (CTE)?

A CTE is a temporary result set that can be referenced within a SELECT, INSERT, UPDATE, or DELETE statement. It is defined using the WITH keyword and is useful for breaking complex queries into more understandable parts.

Unlike a regular subquery (which is embedded within the main query), a CTE can be referenced multiple times within the query. Once the query execution is complete, the CTE's result set disappears.


Benefits of CTE:

Readability: Simplifies complex queries.
Reusability: Can reference the result multiple times.
Recursive Capability: Recursive CTEs allow hierarchical or recursive queries, such as fetching an employee hierarchy.


2. How is a CTE different from a subquery or a derived table?

Subqueries: A query inside another query, typically used for filtering or aggregation. It is nested directly within a SELECT, INSERT, UPDATE, or DELETE statement.

Example of a subquery:

SELECT employee_id, name
FROM employees
WHERE department_id = (SELECT department_id FROM departments WHERE department_name = 'HR');
Enter fullscreen mode Exit fullscreen mode

Derived Tables: Derived tables are also subqueries but are often used in the FROM clause to act as a table.

Example of a derived table:

SELECT temp.employee_id, temp.salary
FROM (SELECT employee_id, salary FROM employees WHERE salary > 5000) temp;

Enter fullscreen mode Exit fullscreen mode

CTE: A CTE is defined using the WITH clause and can be referenced just like a temporary table. Unlike subqueries, CTEs can be recursive and can be used to simplify complex operations.

Example:

WITH high_earners AS (
    SELECT employee_id, salary
    FROM employees
    WHERE salary > 5000
)
SELECT * FROM high_earners;
Enter fullscreen mode Exit fullscreen mode

Key Difference:

CTEs allow recursion and multiple references, while subqueries and derived tables do not.
CTEs are more readable for breaking down complex queries.


3. Types of CTEs: Non-Recursive vs Recursive CTEs

Non-Recursive CTEs:

These are standard CTEs that do not reference themselves. They are primarily used to simplify complex queries or to compute intermediate results that are used in the main query.

Example:


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

Enter fullscreen mode Exit fullscreen mode

Here, the CTE department_avg_salary calculates the average salary for each department. The main query then selects employees whose salaries are greater than their department's average.


Recursive CTEs:

Recursive CTEs reference themselves and are used for querying hierarchical data like organizational charts, part trees, etc. They work by defining an anchor member (the initial rowset) and a recursive member (the rows that reference the CTE itself).

Example:

WITH RECURSIVE employee_hierarchy AS (
    -- Anchor Member: Select the top-level manager
    SELECT employee_id, name, manager_id
    FROM employees
    WHERE manager_id IS NULL

    UNION ALL

    -- Recursive Member: Find employees managed by the previous level
    SELECT e.employee_id, e.name, e.manager_id
    FROM employees e
    JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;

Enter fullscreen mode Exit fullscreen mode

Anchor Member: The initial result set (top-level managers with manager_id IS NULL).

Recursive Member: Fetches employees managed by the employees from the previous level.


4. CTE Syntax Explanation

The basic syntax for a CTE is as follows:


WITH cte_name AS (
    -- CTE Query
    SELECT column1, column2
    FROM table_name
    WHERE condition
)
SELECT column1, column2
FROM cte_name
WHERE condition;

Enter fullscreen mode Exit fullscreen mode
WITH: Keyword to start defining the CTE.
cte_name: Temporary result set (acts like a table)
CTE Query: The query inside the CTE that defines its result.
Main Query: The main query references the CTE as if it were a table.
Enter fullscreen mode Exit fullscreen mode

5. Writing a Simple Query Using a CTE

Here’s an example using a CTE:
Problem:

Find employees earning more than the average salary in their department.

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

Enter fullscreen mode Exit fullscreen mode

Explanation:

  1. The CTE department_avg_salary calculates the average salary per department.
  2. The main query selects employees whose salary is greater than the average salary for their department.

6. Recursive CTE Example

Problem:

You want to list all employees reporting to a specific manager in a hierarchical organization.

WITH RECURSIVE employee_hierarchy AS (
    -- Base case: Select the top-level manager
    SELECT employee_id, name, manager_id
    FROM employees
    WHERE manager_id = :manager_id

    UNION ALL

    -- Recursive case: Select employees reporting to previous level employees
    SELECT e.employee_id, e.name, e.manager_id
    FROM employees e
    JOIN employee_hierarchy eh ON e.manager_id = eh.employee_id
)
SELECT * FROM employee_hierarchy;
Enter fullscreen mode Exit fullscreen mode

The anchor member starts the recursion by finding the manager.

The recursive member fetches employees reporting to the manager and continues the process recursively.


  1. CTE Performance Considerations

CTE improves readability, not necessarily performance.

CTEs are not indexed, so complex CTE queries may not perform better than using regular subqueries or derived tables.

Recursive CTEs may lead to performance degradation if the recursion is too deep or involves too many rows.


9. Use Cases for Recursive CTEs

Hierarchical Data Queries: For example, an organizational chart where each employee has a manager, forming a hierarchy.

Bill of Materials (BOM): To list parts and sub-parts in a manufacturing assembly.

Pathfinding: For finding relationships between entities in a network (like friend connections on social media).


11. Limitations of CTEs

Not Cached: CTE results are not cached in memory, so if you reference them multiple times, the database may recompute the result each time.

No Indexing: CTEs cannot be indexed, which might make queries slower compared to using indexed tables or materialized views.

Performance: Recursive CTEs can degrade performance, especially with deep recursion or large data sets.


This detailed explanation should give you a solid understanding of CTEs and how they are used in SQL and PL/SQL!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .