Skip to content

Pattern 3: CTEs (Common Table Expressions) & Recursive Queries

  • CTEs are crucial for writing clean, readable queries.
  • Recursive CTEs are a key test for handling hierarchical data.

Easy

1. Using a CTE, find the department with the highest average salary.

Department Table:

| department_id | department_name  |
|---------------|------------------|
| 1             | Engineering      |
| 2             | Human Resources  |
| 3             | Sales            |
| 4             | Marketing        |
| 5             | Finance          |

Employees Table:

| employee_id | employee_name  | salary    | department_id |
|-------------|----------------|-----------|---------------|
| 1           | Alice Johnson  | 95000.00  | 1             |
| 2           | Bob Williams   | 110000.00 | 1             |
| 3           | Charlie Brown  | 85000.00  | 1             |
| 4           | Diana Prince   | 120000.00 | 1             |
| 5           | Eve Adams      | 65000.00  | 2             |
| 6           | Frank White    | 70000.00  | 2             |
| 7           | Grace Hall     | 68000.00  | 2             |
| 8           | Henry Clark    | 80000.00  | 3             |
| 9           | Ivy Lewis      | 82000.00  | 3             |
| 10          | Jack Robinson  | 78000.00  | 3             |
| 11          | Karen Walker   | 90000.00  | 3             |
| 12          | Liam Scott     | 72000.00  | 4             |
| 13          | Mia King       | 75000.00  | 4             |
| 14          | Noah Green     | 71000.00  | 4             |
| 15          | Olivia Harris  | 100000.00 | 5             |
| 16          | Peter Wright   | 105000.00 | 5             |
| 17          | Quinn Hill     | 98000.00  | 5             |

SQL

with department_salary as (
    SELECT 
        e.department_id,
        d.department_name,
        AVG(e.salary) as avg_salary
    FROM employees e
        INNER JOIN departments d on d.department_id = e.department_id
    GROUP BY e.department_id, d.department_name
)

SELECT 
    department_id, 
    department_name, 
    avg_salary
from department_salary
ORDER BY avg_salary DESC LIMIT 1;

Result:

| department_id | department_name | avg_salary |
|---------------|-----------------|------------|
| 1             | Engineering     | 102500.00  |

2. Breaking Down a Join

  • Rewrite a query that joins three tables (customers, orders, order_details) to calculate total revenue per customer, using a CTE to first aggregate the order_details.

Customers Table

| customer_id | customer_name |
|-------------|---------------|
| 1           | John Smith    |
| 2           | Jane Doe      |
| 3           | Peter Jones   |

Orders Table

| order_id | customer_id | order_date  |
|----------|-------------|-------------|
| 1        | 1           | 2023-10-01  |
| 2        | 1           | 2023-10-15  |
| 3        | 2           | 2023-10-05  |
| 4        | 3           | 2023-10-20  |

Order Details

| order_id | order_value |
|----------|-------------|
| 3        | 650.00      |
| 4        | 190.00      |
| 2        | 75.00       |
| 1        | 1225.00     |

SQL

WITH order_value_data AS (
SELECT
    od.order_id,
    SUM(od.quantity * price_per_unit) as order_value
from order_details od
GROUP BY od.order_id
)

SELECT
    o.customer_id,
    SUM(ov.order_value) as revenue
from order_value_data as ov
INNER JOIN orders o on ov.order_id = o.order_id
INNER JOIN customers c on c.customer_id = o.customer_id
GROUP BY o.customer_id
ORDER BY revenue DESC

Result:

| customer_id | revenue |
|-------------|---------|
| 1           | 1300.00 |
| 2           | 650.00  |
| 3           | 190.00  |