Glossary

Common Table Expression (CTE)

Long SQL queries do not have to be confusing.

A common table expression (CTE) helps you name a temporary result and use it like a table inside a larger query. You define it once, use it multiple times, and keep your logic simple and easy to follow.

CTEs are useful when working with recursive data, running joins, or repeating the same calculation across different parts of a query.

Instead of repeating subqueries, CTEs let you split your logic into clear steps. They run once, last only for that query, and disappear after.

They make complex SQL easier to write and understand.

What Is a Common Table Expression?

A common table expression is a temporary result you define at the start of a SQL query.

It starts with the WITH keyword, which lets you name a table expression. That result works like a table during the query but is not stored afterward.

This helps in two ways:

  • It keeps your SQL cleaner and easier to manage
  • It lets you reuse logic more than once in the same query

Here is a simple example:

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

In this case, recent_orders is a temporary table you can use in your main query. You define it once and use it as many times as needed.

CTEs work with SELECT, INSERT, UPDATE, DELETE, and MERGE. You can use them for reports, data changes, or recursive problems.

Recursive CTEs are more advanced. They can reference themselves and help with tasks like walking through parent-child data. To stay safe, most systems let you set a limit with MAXRECURSION so your query does not get stuck in a loop.

Why Use a CTE Instead of a Subquery?

Subqueries work, but they can get messy.

When you repeat the same logic in different places, your query gets long and hard to read. With a CTE, you define that logic once and give it a name.

This makes it easier to debug and maintain your SQL.

CTEs work great when:

  • You need to do the same calculation more than once
  • You are joining multiple layers of data
  • You are breaking a big task into small steps

Because the CTE is defined at the top of your query, it is easy to see what it does. It will not affect your database or your schema. It just runs, gives a result, and disappears.

How a Common Table Expression Works

Every CTE starts with WITH.

You give it a name, define the columns (optional), and write a query that returns rows. This result is temporary and only used in the rest of your query.

WITH cte_name (column1, column2) AS (
  SELECT column1, column2
  FROM some_table
  WHERE condition = true
)
SELECT column1
FROM cte_name;



You can also create more than one CTE. Just separate them with commas.

WITH filtered_data AS (
  SELECT * FROM sales WHERE region = 'West'
),
aggregated_data AS (
  SELECT salesperson, SUM(amount) AS total
  FROM filtered_data
  GROUP BY salesperson
)
SELECT * FROM aggregated_data WHERE total > 10000;



You can join CTEs, add filters, or use them in calculations. You can use them with any SQL operation.

Recursive CTEs have two parts: a starting part and a repeating part. They build one layer at a time.

WITH RECURSIVE hierarchy (id, parent_id, level) AS (
  SELECT id, parent_id, 1
  FROM org_chart
  WHERE parent_id IS NULL
  UNION ALL
  SELECT o.id, o.parent_id, h.level + 1
  FROM org_chart o
  JOIN hierarchy h ON o.parent_id = h.id
)
SELECT * FROM hierarchy;


This helps with tree data, like categories or reporting structures. Be sure to use a stop condition or a limit so it does not run forever.

When to Use a Common Table Expression

CTEs are helpful when your SQL gets complicated.

Use them when:

  • You need to run the same subquery more than once
  • Your query is getting too long or too hard to follow
  • You need to split a problem into smaller steps
  • You are working with recursive or tree-style data
  • You want to stage data before updating or deleting

For example, if you are doing totals, filters, and joins all in one query, it can be hard to follow. CTEs let you break it down step by step.

They also help when you want to apply changes to your data based on a temporary result set.

Recursive Common Table Expressions

Recursive CTEs let you work with data that has layers.

Think of employee-to-manager charts, file folders, or product parts. A recursive CTE starts with a base case and builds layer by layer using the result of the last step.

WITH RECURSIVE tree (id, parent_id, level) AS (
  SELECT id, parent_id, 1
  FROM categories
  WHERE parent_id IS NULL
  UNION ALL
  SELECT c.id, c.parent_id, t.level + 1
  FROM categories c
  JOIN tree t ON c.parent_id = t.id
)
SELECT * FROM tree;


Use a WHERE clause or a setting like MAXRECURSION to avoid infinite loops.

Real-World Examples

1. Reporting sales and targets

WITH total_sales AS (
  SELECT salesperson_id, SUM(amount) AS sales
  FROM sales
  GROUP BY salesperson_id
),
sales_quota AS (
  SELECT salesperson_id, target
  FROM quotas
)
SELECT 
  t.salesperson_id,
  t.sales,
  q.target,
  t.sales - q.target AS difference
FROM total_sales t
JOIN sales_quota q ON t.salesperson_id = q.salesperson_id;




2. Chaining logic in steps

WITH recent_purchases AS (
  SELECT customer_id, product_id, purchase_date
  FROM purchases
  WHERE purchase_date >= CURRENT_DATE - INTERVAL '30 days'
),
purchase_counts AS (
  SELECT customer_id, COUNT(*) AS order_count
  FROM recent_purchases
  GROUP BY customer_id
)
SELECT *
FROM purchase_counts
WHERE order_count > 5;

3. Updating based on a CTE

WITH base_components AS (
  SELECT product_id, component_id
  FROM product_assemblies
  WHERE level = 0
)
UPDATE product_assemblies
SET quantity = quantity * 2
FROM base_components b
WHERE product_assemblies.component_id = b.component_id;

4. Using a CTE instead of a temp table

WITH temp_metrics AS (
  SELECT customer_id, AVG(order_total) AS avg_order
  FROM orders
  GROUP BY customer_id
)
SELECT *
FROM temp_metrics
WHERE avg_order > 100;

FAQ

What is a CTE?

It is a temporary result created using WITH in SQL. You can reuse it in the same query.

Can I create more than one CTE?

Yes. Just separate them with commas.

Are CTEs stored in the database?

No. They only exist while the query is running.

What is a recursive CTE?

A CTE that calls itself to work with layered data like trees or hierarchies.

How do I stop a recursive CTE from running forever?

Use a WHERE clause or set a MAXRECURSION limit.

Which SQL statements work with CTEs?

CTEs work with SELECT, INSERT, UPDATE, DELETE, and MERGE.

Can I use joins, filters, and groups inside a CTE?

Yes. You can treat it like any other subquery.

Can a CTE reference another CTE?

Yes, as long as the one being referenced appears first.

Do all databases support CTEs?

Most do, including SQL Server, PostgreSQL, MySQL 8 and later, Oracle, and Snowflake.

Summary

A common table expression (CTE) helps you write SQL that is clean and easy to follow. You can reuse logic, stage complex steps, and work with recursive data all inside a single query.

It is perfect for:

  • Breaking down hard queries
  • Avoiding repeated subqueries
  • Working with trees and hierarchies
  • Making your SQL easier to read and update

If your query is doing more than pulling a few columns, try using a CTE. It will help you stay organized and write better SQL.

A wide array of use-cases

Trusted by Fortune 1000 and High Growth Startups

Pool Parts TO GO LogoAthletic GreensVita Coco Logo

Discover how we can help your data into your most valuable asset.

We help businesses boost revenue, save time, and make smarter decisions with Data and AI