Back to Articles

Common Table Expressions (CTEs) Explained

Learn how to simplify complex queries and improve readability with SQL's Common Table Expressions.

What is a Common Table Expression?

A Common Table Expression (CTE) is a temporary result set that can be referenced within a SQL query. It allows you to break down complex queries into smaller, more manageable parts, improving readability and maintainability.

CTEs are defined using the WITH keyword, followed by a name for the CTE and a subquery enclosed in parentheses. The result of this subquery can then be used as if it were a table in the main query.

How CTEs Work

When you define a CTE, it is only available for the duration of the query execution. Unlike temporary tables, CTEs do not persist beyond the query itself. This makes them a great tool for structuring queries without cluttering the database.

The syntax of a CTE follows this structure:

-- Basic CTE
WITH recent_orders AS (
    SELECT order_id, customer_id, order_date
    FROM orders
    WHERE order_date >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT * FROM recent_orders;

Here’s what happens step-by-step:

  • The WITH clause defines the CTE.
  • A name is assigned to the CTE (in this case, recent_orders).
  • The subquery inside the parentheses is executed, generating the temporary dataset.
  • The main query then references the CTE as if it were a regular table.

Recursive CTEs

Recursive CTEs are used to work with hierarchical or tree-structured data, such as organizational charts or file systems. They reference themselves within the query to repeatedly execute until a stopping condition is met.

-- Recursive CTE for Hierarchical Data
WITH RECURSIVE employee_hierarchy AS (
    SELECT employee_id, manager_id, employee_name, 1 AS level
    FROM employees
    WHERE manager_id IS NULL
    
    UNION ALL
    
    SELECT e.employee_id, e.manager_id, e.employee_name, h.level + 1
    FROM employees e
    INNER JOIN employee_hierarchy h ON e.manager_id = h.employee_id
)
SELECT * FROM employee_hierarchy;

Using Multiple CTEs

SQL allows defining multiple CTEs within a single WITH clause. This is useful for breaking down complex logic into structured parts.

-- Multiple CTEs
WITH customers_with_orders AS (
    SELECT customer_id, COUNT(*) AS order_count
    FROM orders
    GROUP BY customer_id
),

high_value_customers AS (
    SELECT customer_id
    FROM customers_with_orders
    WHERE order_count > 10
)
SELECT * FROM high_value_customers;