SQL SERVER

SQL Server CTE: Everything You Need to Know

intro

Let’s explore everything you need to know about SQL Server CTE statements, including what they are and how they can help you write more readable queries.

Tools used in the tutorial
Tool Description Link
Dbvisualizer DBVISUALIZER
TOP RATED DATABASE MANAGEMENT TOOL AND SQL CLIENT

To extract the data of interest from an SQL Server database, you might need to run multiple queries. Executing them one at a time at the application level can result in significant overhead. While SQL subqueries can help, they often fall short in terms of reusability. This is where a WITH clause with the right SQL Server CTE statements can make a difference!

In this article, you will look at the definition of a CTE in SQL server, how to use the WITH clause, and see some examples of regular and recursive CTEs.

Let's dive in!

What Is a CTE in SQL Server?

A CTE, short for Common Table Expression, is a temporary named result set that you can reference within the execution scope of a SELECT, INSERT, UPDATE, or DELETE statement. An SQL Server CTE is defined using the WITH keyword, followed by the CTE name and a query that defines its result set.

In SQL Server, a common table expression can also include references to itself. This is referred to as a recursive common table expression (or recursive CTE).

The CTE SQL Server clause can also be used in a CREATE VIEW statement as part of its defining SELECT statement. Find out more in our guide on SQL views.

How to Define a Common Table Expression in SQL Server

In SQL Server, a CTE is defined through the WITH clause. Each SQL Server CTE consists of two elements:

  1. A name: Used to refer to the CTE result set in the main query.
  2. A query: Used to define how the CTE retrieves the desired data.

Here is the high-level syntax to define an SQL Server common table expression:

Copy
        
1 WITH cte_name (column_1, column_2, ..., column_N) AS ( 2 cte_definition_query 3 ) 4 main_query;

Where:

  • cte_name is the name given to the CTE SQL server statement.
  • column_1, column_2, ..., column_N is the list of column names in the result set of the common table expression. The number of column names in this list must match the number of columns in the result set of cte_definition_query. The list of column names is optional only if distinct names for all resulting columns are supplied in cte_definition_query. Duplicate names within a single CTE definition are not allowed. The names specified in this list work as SQL aliases for the columns selected in cte_query_definition.
  • cte_definition_query is a SELECT statement whose result set populates the CTE. This query can consist of multiple queries joined using the UNION ALL, UNION, EXCEPT, or INTERSECT operators.
  • main_query is the SELECT, INSERT, UPDATE, or DELETE statement referencing the CTE.

For a more complete syntax, refer to the sample SELECT query below with a CTE:

Copy
        
1 WITH cte_name (column_1, column_2, ..., column_N) AS ( 2 SELECT column_a, column_b, ..., column_n 3 FROM table_name 4 WHERE cte_condition 5 ) 6 SELECT * 7 FROM cte_name 8 WHERE query_condition;

Note that the same WITH clause can define several CTEs, each with its name and specification query:

Copy
        
1 WITH cte_name_1 (column_1, column_2, ..., column_N) AS ( 2 SELECT column_a, column_b, ..., column_n 3 FROM table_name_1 4 WHERE cte_condition_1 5 ), 6 ... 7 cte_name_M (column_1, column_2, ..., column_N) AS ( 8 SELECT column_x, column_y, ..., column_n 9 FROM table_name_2 10 WHERE cte_condition_2 11 ) 12 SELECT * 13 FROM table_X 14 WHERE query_condition;

The main query can then use the result sets of the different CTEs, for example, to join them or use them in IN operators within WHERE conditions.

Key Aspects of an SQL Server CTE

  • A CTE exists only for the duration of the query it is part of. In other words, a CTE is not stored in the database.
  • You can reference a CTE multiple times within the same query.
  • SQL Server allows recursive CTE definitions. In this case, the CTE references itself.
  • A CTE SQL Server statement cannot define another CTE.
  • A CTE can be specified in a CREATE VIEW statement, as part of the defining SELECT statement of the view.

Guidelines for Creating and Using CTEs in SQL Server

To properly create and work with Common Table Expressions in SQL Server, you need to follow key rules some of which are as follows:

  1. A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE statement.
  2. A CTE can reference itself and previously defined CTEs. At the same time, forward referencing is not allowed.
  3. You cannot specify more than one WITH clause in a CTE. For example, if a CTE definition query contains a subquery, that subquery cannot contain a nested WITH clause that defines another CTE. Discover more in our article on SQL subqueries.
  4. ORDER BY (except when a TOP clause is specified), INTO, OPTION, and FOR BROWSE clauses cannot be used in the CTE definition query.

Adherence to these four CTE guidelines and rules will make your job as a DBA easier. For all guidelines, refer to the official documentation.

Guidelines for Creating and Using Recursive CTEs in SQL Server

  • A recursive CTE requires at least two query definitions: an anchor member and a recursive member.
  • Multiple anchor and recursive members can be defined, but all anchor members must precede the first recursive member.
  • Anchor members are combined using set operators like UNION ALL, UNION, INTERSECT, or EXCEPT.
  • The number of columns in the anchor and recursive members must match.
  • The data types of corresponding columns in the anchor and recursive members must be the same.
  • The FROM clause of a recursive member can reference the CTE name only once.
  • The following clauses are not allowed in a recursive member's query definition: SELECT DISTINCT, GROUP BY, PIVOT, HAVING, TOP, subqueries, scalar aggregations, and LEFT, RIGHT, or OUTER JOINs.
  • All columns in a recursive CTE are nullable, regardless of the nullability of the original column.

For all other guidelines, check out the official documentation.

SQL Server CTE Example List

Now that you know what a CTE is and how it works in SQL Server, you are ready to explore some SQL Server CTE examples!

Note: The sample query below will be executed in DbVisualizer, a fully-featured database client that fully supports SQL Server and dozens of other databases. Anyway, any other database client will do.

Example 1: Getting an Organization’s Hierarchy

Assume your Employees table has the EmployeeID, FullName, and ManagerID columns. Your goal is to get the organizational hierarchy under a specific employee.

Here's how you can get that data using a recursive CTE in SQL Server:

Copy
        
1 WITH EmployeeHierarchy AS ( 2 SELECT EmployeeID, FullName, ManagerID 3 FROM Employees 4 WHERE EmployeeID = 3 5 UNION ALL 6 SELECT E.EmployeeID, E.FullName, E.ManagerID 7 FROM Employees E 8 JOIN EmployeeHierarchy EH 9 ON EH.EmployeeID = E.ManagerID 10 ) 11 SELECT * 12 FROM EmployeeHierarchy;

In this query, the CTE EmployeeHierarchy selects the employee with ID 3. Then, it recursively joins the partial result set with the Employees table to retrieve the employees managed by the current manager, continuing to traverse the hierarchy until the entire sub-hierarchy is fetched. This is a common query pattern to traverse a tree structure in SQL Server.

Execute it, and you will get:

Note the employee hierarchy in the result set
Note the employee hierarchy in the result set

Note that you cannot achieve the same result with a simple SELECT query.

Example 2: Retrieving the Department with the Highest Average Salary

Suppose your Employees table has the EmployeeID, DepartmentID, and Salary columns. You want to find out which department has the highest average salary.

You can obtain that information using a CTE SQL Server statement as below:

Copy
        
1 WITH DepartmentAvgSalary AS ( 2 SELECT DepartmentID, AVG(Salary) AS AvgSalary 3 FROM Employees 4 GROUP BY DepartmentID 5 ) 6 SELECT TOP 1 D.DepartmentName, DAS.AvgSalary 7 FROM DepartmentAvgSalary DAS 8 JOIN Departments D ON D.DepartmentID = DAS.DepartmentID 9 ORDER BY DAS.AvgSalary DESC;

Here, the CTE DepartmentAvgSalary calculates the average salary for each department. The main query then displays the department with the highest average salary.

Launch it in DbVisualizer and you will obtain:

Finance is the department with the highest average salary
Finance is the department with the highest average salary

Example 3: Finding Per-Product Sales in Top Regions

Now, you want to get sales totals by product only in the top-selling regions. You can use an SQL Server CTE involving two auxiliary subqueries as follows:

Copy
        
1 WITH RegionalSales AS ( 2 SELECT RegionID, SUM(PriceAmount) AS TotalSales 3 FROM Orders 4 GROUP BY RegionID 5 ), TopRegions AS ( 6 SELECT RegionID 7 FROM RegionalSales 8 WHERE TotalSales > ( 9 SELECT SUM(TotalSales) / (SELECT COUNT(*) FROM Regions) 10 FROM RegionalSales 11 ) 12 ) 13 SELECT R.RegionName, O.ProductName, SUM(O.Quantity) AS ProductUnits, SUM(O.PriceAmount) AS ProductSales 14 FROM Orders O 15 JOIN Regions R ON O.RegionID = R.RegionID 16 WHERE O.RegionID IN ( 17 SELECT RegionID 18 FROM TopRegions 19 ) 20 GROUP BY R.RegionName, O.ProductName 21 ORDER BY RegionName;

In this example, the first CTE — RegionalSales — calculates total sales per region. Next, the second CTE — TopRegions — identifies the regions with more sales than the average. The main query finally uses the TopRegions output in the WHERE clause to retrieve sales data only for those top regions.

The result will be:

Europe and North America are the top-selling regions
Europe and North America are the top-selling regions

Et voilà! You are now an SQL Server CTE expert!

Conclusion

In this article, you learned what a CTE in SQL Server is and how to define one. You now understand that common table expressions are a powerful tool for improving query organization and facilitating the reuse of SQL code.

CTEs make your queries longer and more complex, so the need for an advanced database client becomes essential. This is where DbVisualizer comes into play!

Beyond offering the most common features of a database client and support for dozens of DBMSs, DbVisualizer provides advanced query optimization capabilities that can take your SQL Server CTEs to the next level. Download DbVisualizer for free now!

FAQ

How does an SQL Server CTE differ from a regular subquery?

An SQL Server CTE differs from a regular subquery because it allows for easier code reuse within complex queries. CTEs are defined at the beginning of a query using the WITH clause and can be referenced multiple times within the main query. In contrast, regular subqueries are embedded within a single query and cannot be reused or referenced elsewhere in the same query.

What is an SQL Server recursive CTE?

An SQL Server recursive CTE is a type of CTE that references itself to repeatedly execute a query. It consists of two parts: an anchor member, which provides the initial result set, and a recursive member, which iterates over the result set to add subsequent rows. The recursion continues until a specified condition is met. The official docs will provide more information.

Can a single SQL Server query have multiple CTEs?

Yes, a single SQL Server query can have multiple CTEs. You can define several CTEs in a query by separating each with a comma after the initial WITH keyword. Each CTE can be referenced in the main query or within other CTEs.

What is the difference between a CTE and a recursive CTE in SQL Server?

The key difference between a CTE and a recursive CTE in SQL Server is that a regular CTE does not involve recursion while a recursive CTE is designed to perform iterative operations until a condition is met.

What are the pros and cons of WITH queries in SQL Server?

👍 Pros:

  • Improved readability: WITH clauses make complex queries easier to read and understand by breaking them into manageable parts.
  • Enhanced reusability: CTEs can be referenced multiple times within the same query, reducing redundancy and improving code efficiency.
  • Facilitating recursive operations: CTEs make it possible to achieve recursion in SQL Server.

👎 Cons:

  • Temporary scope: CTEs are limited to the query in which they are defined, meaning they cannot be reused across multiple queries.
  • Potential performance issues: CTEs can lead to slower query performance, especially with large datasets or complex recursive operations.
  • Limited indexing: CTEs do not support indexing, which can result in less efficient data retrieval. With that being said, CTEs can use indexes from underlying tables. In other words, your database can use indexes if they are defined on a table.
Dbvis download link img
About the author
Antonello Zanini

Antonello is a software engineer, and often refers to himself as a technology bishop. His mission is to spread knowledge through writing.

The Table Icon
Sign up to receive The Table's roundup
More from the table
Title Author Tags Length Published
title

How and Why to Add an Index in SQL

author Lukas Vileikis tags MySQL POSTGRESQL Search SQL SQL SERVER 8 min 2024-11-28
title

List of SQL Reserved Words in All Major Databases

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 16 min 2024-11-20
title

SQL Variable: What It Is and How To Use It

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 8 min 2024-11-18
title

SQL Server Guide: Create a View Based on a Stored Procedure

author Antonello Zanini tags SQL SERVER 5 min 2024-11-05
title

The Definitive Guide to the SQL Server GETDATE Function

author Antonello Zanini tags SQL SERVER 6 min 2024-10-31
title

CONCAT_WS SQL Server Function: Ultimate Guide With Examples

author Antonello Zanini tags SQL SERVER 6 min 2024-10-24
title

The Ultimate Guide to the SQL Server Date Format

author Antonello Zanini tags DATE SQL SERVER 9 min 2024-10-23
title

SQL Not Equal Operator: Definitive Guide with Examples

author Antonello Zanini tags MySQL ORACLE POSTGRESQL SQL SQL SERVER 6 min 2024-10-14
title

SQL Server Date Diff: Calculating the Difference Between Two Dates

author Antonello Zanini tags SQL SERVER 6 min 2024-10-07
title

A Complete Guide to the SQL Server FOR XML PATH Clause

author Antonello Zanini tags SQL SERVER XML 8 min 2024-10-01

The content provided on dbvis.com/thetable, including but not limited to code and examples, is intended for educational and informational purposes only. We do not make any warranties or representations of any kind. Read more here.

Cookie policy

We use cookies to ensure that we give you the best experience on our website. However you can change your cookie settings at any time in your browser settings. Please find our cookie policy here ↗