Define temporary result sets that can be referenced within a SELECT, INSERT, UPDATE, or DELETE statement.
They work almost like subqueries.
Although, subqueries are typically used within the context of a larger query and are not reusable, whereas CTEs are reusable within the same query.
CTES are defined by adding a WITH statement before SELECT, UPDATE, INSERT, DELETE, MERGE statements. The WITH clause can contain one or more CTEs separated by commas.
syntax:
WITH cte_name (column1, column2, ...) AS (
-- SQL query that defines the CTE
)
WITH shows that its CTE query
cte_name: acts as the alias
Columns 1 and 2 are the columns to be derived which is an optional parameter. If ignored the CTE will inherit the columns from the query defining it.
The information after the AS is usually the subquery being executed
Example in code:
WITH EmployeeCTE AS (
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Department = 'IT'
)
SELECT FirstName, LastName
FROM EmployeeCTE
WHERE Salary > 50000;
Why CTES:
Enables users to more easily write and maintain complex queries. Each CTE can have a clear, descriptive name that explains its purpose, making it easier for other developers (or your future self) to understand the intention behind each part of the query.
Reduction of Redundancy: CTEs promote code reusability. When you have a complex operation that needs to be performed multiple times in a query, you can define it once in a CTE and then reference that CTE multiple times within the same query.
Help perform multi-level aggregations. For instance, you might need to calculate monthly totals, and then aggregate those into quarterly or annual totals. CTEs allow you to create distinct levels of aggregation and build upon previous results in a structured and readable manner.
Example:
-- Create a CTE for daily sales WITH DailySales AS ( SELECT DATE_TRUNC('day', sale_date) AS sale_day, SUM(sale_amount) AS daily_total FROM Sales GROUP BY DATE_TRUNC('day', sale_date) ), -- Create a CTE for monthly sales using the DailySales CTE MonthlySales AS ( SELECT DATE_TRUNC('month', sale_day) AS sale_month, SUM(daily_total) AS monthly_total FROM DailySales GROUP BY DATE_TRUNC('month', sale_day) ), -- Create a CTE for yearly sales using the MonthlySales CTE YearlySales AS ( SELECT DATE_TRUNC('year', sale_month) AS sale_year, SUM(monthly_total) AS yearly_total FROM MonthlySales GROUP BY DATE_TRUNC('year', sale_month) ) -- Query the YearlySales CTE to get the final result SELECT EXTRACT(YEAR FROM sale_year) AS year, yearly_total FROM YearlySales ORDER BY year;
In the above query, we are trying to classify the sales either as daily, monthly or yearly sales.
Make code easier to debug and more visually appealing.
Types of CTEs
There are two types:
Recursive CTE: references itself. By doing so, the CTE repeatedly executes and returns subsets of information
Used when working with hierarchical data, such as organizational charts or tree-like structures.
WITH RecursiveCTE (n) AS ( SELECT 1 UNION ALL SELECT n + 1 FROM RecursiveCTE WHERE n < 10 ) SELECT n FROM RecursiveCTE;
- Nonrecursive CTEs:
used for general purposes and don't reference themselves.
WITH EmployeeCTE AS (
SELECT FirstName, LastName, Salary
FROM Employees
WHERE Department = 'IT'
)
SELECT FirstName, LastName
FROM EmployeeCTE
WHERE Salary > 50000;