What if you could turn a messy, multi-step data task into a clean, easy-to-follow query in seconds?
Why Multiple CTEs in one query in SQL? - Purpose & Use Cases
Imagine you have a big spreadsheet with sales data, customer info, and product details all mixed up. You want to find the top customers by sales, but first, you need to clean and organize the data step-by-step. Doing this by hand means copying and filtering data multiple times across sheets.
Manually filtering and organizing data is slow and easy to mess up. You might forget a step or mix up numbers. It's hard to keep track of changes, and if the data updates, you have to redo everything from scratch.
Using multiple CTEs (Common Table Expressions) in one query lets you break down complex tasks into clear, manageable steps inside the database. Each CTE acts like a mini-table you can build on, making your query easier to read and maintain.
SELECT * FROM sales WHERE date > '2023-01-01'; -- Then manually join with customers and products in separate steps
WITH recent_sales AS (
SELECT * FROM sales WHERE date > '2023-01-01'
), top_customers AS (
SELECT customer_id, SUM(amount) AS total FROM recent_sales GROUP BY customer_id
)
SELECT * FROM top_customers;It enables you to write clear, step-by-step queries that handle complex data tasks all at once, saving time and reducing errors.
A business analyst can quickly find the top-selling products last quarter by first filtering sales data, then summarizing totals, and finally joining with product info--all in one neat query.
Manual data handling is slow and error-prone.
Multiple CTEs let you organize complex queries into simple steps.
This approach makes queries easier to read, maintain, and update.
Practice
Solution
Step 1: Understand the role of CTEs
CTEs are temporary named result sets used inside a query to organize complex logic.Step 2: Identify the benefit of multiple CTEs
Using multiple CTEs lets you split a big query into smaller, readable parts without permanent storage or indexing.Final Answer:
To break down complex queries into simpler, named parts for better readability -> Option BQuick Check:
Multiple CTEs improve query clarity = D [OK]
- Thinking CTEs create permanent tables
- Assuming CTEs improve query speed automatically
- Confusing CTEs with indexes
cte1 and cte2 in one SQL query?Solution
Step 1: Recall correct CTE syntax
Multiple CTEs are separated by commas after WITH, each defined asname AS (query).Step 2: Check each option
WITH cte1 AS (SELECT * FROM table1), cte2 AS (SELECT * FROM table2) SELECT * FROM cte1 JOIN cte2 ON cte1.id = cte2.id; correctly uses commas and AS keyword; others miss commas or use wrong syntax.Final Answer:
WITH cte1 AS (SELECT * FROM table1), cte2 AS (SELECT * FROM table2) SELECT * FROM cte1 JOIN cte2 ON cte1.id = cte2.id; -> Option AQuick Check:
Multiple CTEs separated by commas = B [OK]
- Omitting commas between CTEs
- Using semicolons between CTEs
- Using equals sign instead of AS
WITH cte1 AS (SELECT 1 AS num UNION ALL SELECT 2), cte2 AS (SELECT num FROM cte1 WHERE num = 2) SELECT * FROM cte2;
Solution
Step 1: Analyze cte1 content
cte1 returns two rows with num values 1 and 2.Step 2: Analyze cte2 filtering
cte2 selects only rows from cte1 where num = 2, so only one row with num=2.Final Answer:
2 -> Option CQuick Check:
cte2 filters num=2 = A [OK]
- Assuming cte2 returns all rows from cte1
- Confusing UNION ALL with UNION
- Ignoring the WHERE filter in cte2
WITH cte1 AS (SELECT id, name FROM users) SELECT * FROM cte1; WITH cte2 AS (SELECT id FROM orders) SELECT * FROM cte2;
Solution
Step 1: Review CTE syntax rules
Only one WITH clause is allowed per query; multiple CTEs must be separated by commas inside one WITH.Step 2: Identify the error in the query
The query uses two separate WITH clauses, which is invalid syntax.Final Answer:
Multiple WITH clauses are not allowed in one query -> Option DQuick Check:
Only one WITH clause per query = A [OK]
- Writing multiple WITH clauses in one query
- Forgetting commas between CTEs
- Assuming CTEs create permanent tables
Solution
Step 1: Calculate average order per customer
WITH avg_orders AS (SELECT customer_id, AVG(amount) AS avg_amount FROM orders GROUP BY customer_id), high_avg AS (SELECT customer_id FROM avg_orders WHERE avg_amount > 100) SELECT * FROM high_avg; correctly groups orders by customer_id and calculates AVG(amount).Step 2: Filter customers with avg_amount > 100
WITH avg_orders AS (SELECT customer_id, AVG(amount) AS avg_amount FROM orders GROUP BY customer_id), high_avg AS (SELECT customer_id FROM avg_orders WHERE avg_amount > 100) SELECT * FROM high_avg; uses a second CTE to filter customers with average above 100, then selects from it.Final Answer:
WITH avg_orders AS (SELECT customer_id, AVG(amount) AS avg_amount FROM orders GROUP BY customer_id), high_avg AS (SELECT customer_id FROM avg_orders WHERE avg_amount > 100) SELECT * FROM high_avg; -> Option AQuick Check:
Multiple CTEs for calculation and filtering = C [OK]
- Missing GROUP BY when calculating averages
- Not filtering on aggregated column
- Incorrect CTE definitions without commas
Ready to prove it?
Pass the test and this pattern moves unknown
