CTE (Common Table Expression) and Recursive CTE in SQL Server

Introduction

In this article, you will learn about the common table expression or CTE in SQL Server using the WITH clause.

The common table expression (CTE) is a temporary named result set which is created during query execution and delete once after the query executes. CTE is a temporary table\virtual table with columns and records created during query execution. This can be referenced within a SELECT, INSERT, UPDATE, or DELETE statement.

Why do we use CTE in SQL Server?

  • Simplifies your code
  • Improves the readability of an SQL query
  • The SQL WITH clause allows you to write recursive queries
  • Can use to CREATE a view as part of the view's SELECT query.
  • It can be used inside the SELECT, INSERT, UPDATE, and DELETE types of queries.

CTEs can be a useful tool when you need to generate temporary result sets that can be accessed in a SELECT, INSERT, UPDATE, DELETE, or MERGE statement.

Below is the common Syntax of CTE.

WITH expression_name (columnname1, columnname2)
AS
(
CTE query definition
)
SELECT columnname FROM expression_name

Common Table Expression Types

  • Non-Recursive CTEs: A non-recursive CTE is one that does not reference itself within the CTE. Nonrecursive CTEs tend to be simpler than recursive CTEs
  • Recursive CTEs: The recursive CTE is useful when working with hierarchical data because the CTE continues to execute until the query returns the entire hierarchy.

Now let's create a recursive SQL Server Common Table Expression; lets's create an employee table to demonstrate how recursive CTE works.

CREATE TABLE dbo.Employees
(
  Employee_ID INT NOT NULL PRIMARY KEY,
  First_Name VARCHAR (50) NOT NULL,
  Last_Name VARCHAR (50) NOT NULL,
  Manager_ID INT NULL
)
GO

INSERT INTO Employees VALUES (1, 'Ray', 'Menon', NULL)
INSERT INTO Employees VALUES (2, 'Reshu', 'Dutt', 1)
INSERT INTO Employees VALUES (3, 'Ansh', 'Raj', 1)
INSERT INTO Employees VALUES (4, 'Boby', 'Jain', 2)
INSERT INTO Employees VALUES (5, 'Gargi', 'Jain', 2)
INSERT INTO Employees VALUES (6, 'Aman', 'Mittal', 3)
INSERT INTO Employees VALUES (7, 'Anas', 'Ahamed', 3)
INSERT INTO Employees VALUES (8, 'Dia', 'Mehra', 5)
INSERT INTO Employees VALUES (9, 'Mehak', 'Singh', 5)
INSERT INTO Employees VALUES (10, 'Tara', 'Mehta', 6)
GO

Select for CTE in SQL Server

After the employee's table is created, we will demonstrate how recursive CTE works with a select query

Recursive CTE Example

WITH CTE_Employees (Emp_ID, First_Name, Last_Name, Mgr_ID, Emp_Level)
AS
(
	SELECT Employee_ID, First_Name, Last_Name, Manager_ID, 1
	FROM Employees WHERE Manager_ID IS NULL
	UNION ALL
	SELECT e.Employee_ID, e.First_Name, e.Last_Name, e.Manager_ID, ce.Emp_Level + 1
	FROM Employees e INNER JOIN CTE_Employees ce
	ON e.Manager_ID = ce.Emp_ID
)
	SELECT First_Name, Last_Name, Emp_Level,Mgr_ID
	FROM CTE_Employees 
	ORDER BY Emp_Level, Mgr_ID

Recursive CTE Example

As you can see above

  • CTE returns hierarchical data, and the highest level of the hierarchy is 1, the next level is 2, and then 3, and so on.
  • The CTE query has two SELECT statements and is connected with the UNION ALL operator. A recursive CTE query must contain at least two statements and be connected by UNION ALL, UNION, INTERSECT, or EXCEPT operator.

Let's create a simple recursive CTE that prints numbers from 1 to 9.

Recursive CTE Example

WITH CTE_Num
AS 
(
    SELECT 1 AS Num
    UNION ALL
    SELECT Num + 1 FROM CTE_Num
    WHERE Num < 10
)
	SELECT Num FROM CTE_Num WHERE Num < 10

Recursive CTE Example

Now let's create another recursive CTE to calculate the factorial.

Recursive CTE Example

WITH CTE_Factorial (N, Factorial) 
AS
(
	SELECT 1, 1 
	UNION ALL   -- here is where it gets recursive
	SELECT N + 1, (N + 1) * Factorial FROM CTE_Factorial
	WHERE N < 10
)
	SELECT N , Factorial FROM CTE_Factorial;

Recursive CTE Example

Now let's generate numbers between 1 to 200 by using a recursive CTE by the following script.

Recursive CTE Example

WITH CTE_Num
AS 
(
    SELECT 1 AS Num
    UNION ALL
    SELECT Num + 1 FROM CTE_Num
    WHERE Num < 200
)
	SELECT * FROM CTE_Num;

Recursive CTE Example

The above screenshot shows CTE has a default maximum recursion level of 100. Now let's see how to change the MAXRECURSION level.

The below script is used to change the MAXRECURSION level.

Recursive CTE Example

WITH CTE_Num
AS 
(
    SELECT 1 AS Num
    UNION ALL
    SELECT Num + 1 FROM CTE_Num
    WHERE Num < 200
)
	SELECT * FROM CTE_Num
	OPTION (MAXRECURSION 200)

Recursive CTE Example

The above screenshot shows how to change the CTE's default maximum recursion level.

Now let's consider the Employees and Departments tables again to understand Common Table Expression more.

SELECT * FROM Employees;

Common Table Expression

SELECT * FROM Departments;

Common Table Expression

Let's use the Window function (i.e. Ranking) to understand the CTE more.

We will use the RANK () function here to get department-wise salary rank.

Recursive CTE Example

WITH Dept_Salary_Rank 
AS
(
	SELECT First_Name, Last_Name, Department_id, Salary, 
	RANK () OVER (
	PARTITION BY Department_id ORDER BY Salary) Dept_Salary_Rank
	FROM Employees
)
	SELECT First_Name, Last_Name, r.Department_id, Salary FROM Dept_Salary_Rank r
	INNER JOIN departments d ON d.department_id = r.department_id
	WHERE Dept_Salary_Rank = 1;	

Here is the department-wise salary rank.

Recursive CTE Example

In the above screenshot, we can see the department-wise salary rank.

Now we will execute the complete CTE and see the output.

Recursive CTE Example

As you can see in the above screenshot, we got dept wise lowest salary.

Now we want to get an overall salary rank irrespective of the department, so we use the below CTE script.

Recursive CTE Example

WITH Salary_Rank 
AS
(
	SELECT First_Name, Last_Name, Salary, 
	RANK() OVER (ORDER BY Salary) Salary_Rank
	FROM Employees
)
	SELECT * FROM Salary_Rank

Recursive CTE Example

Now let's create CTE with a DELETE query using the RANK () function.

Recursive CTE Example

WITH Del_Salary_Rank 
AS
(
	SELECT First_Name, Last_Name, Salary, 
	RANK() OVER (ORDER BY Salary) Del_Salary_Rank
	FROM Employees
)
	DELETE FROM Del_Salary_Rank WHERE Salary > = 50000

Recursive CTE Example

Summary

As you can see, CTE is a very useful feature in SQL Server. They simplify the code and can also be used in recursive operations. We have covered a variety of examples to use recursive CTE. Hope you find the article useful. Please share your feedback in the comments section.


Similar Articles