Welcome to Section 4. You already know how to write solid SELECT queries, joins, grouping,
safe filtering, and practical staging with temp tables.
Now we move into the patterns DBAs use for real operations: troubleshooting, building clean scripts,
and writing queries that are easier to maintain under pressure.
Your first tool in this section is the CTE (Common Table Expression).
Lesson description: You will learn what a CTE is, why DBAs use it,
the correct syntax rules, how to chain multiple CTEs, when a CTE is better than a subquery or temp table,
how to use CTEs for de-duplication and “top per group” logic, and how recursive CTEs work conceptually
(with safe examples). You will also learn common mistakes that cause incorrect results or poor performance.
A CTE is a named, temporary result set that exists only for the single statement that follows it.
Think of it as a readable “building block” you can reference like a table—without creating a real table.
DBAs use CTEs primarily to:
Important: A CTE is not a stored object. It does not persist. It is only “visible” to the next statement.
A CTE is written with WITH and must be followed immediately by a single statement
(SELECT/INSERT/UPDATE/DELETE/MERGE).
WITH CTE_Name AS
(
SELECT ...
)
SELECT ...
FROM CTE_Name;
WITH:;WITH ...Scenario: you want to review recent high-value orders in AdventureWorks, but you want your logic to be readable.
A CTE lets you “stage” the first step as a clean dataset, then apply additional filters in the final query.
;WITH RecentOrders AS
(
SELECT
soh.SalesOrderID,
soh.OrderDate,
soh.CustomerID,
soh.TotalDue
FROM Sales.SalesOrderHeader AS soh
WHERE soh.OrderDate >= DATEADD(DAY, -30, CAST(SYSDATETIME() AS date))
)
SELECT
ro.SalesOrderID,
ro.OrderDate,
ro.CustomerID,
ro.TotalDue
FROM RecentOrders AS ro
WHERE ro.TotalDue >= 5000.00
ORDER BY ro.TotalDue DESC;
Why DBAs like this: if a report is wrong, you can quickly inspect the CTE definition and confirm the base dataset.
One of the strongest uses of CTEs is chaining steps. Each CTE is a stage in your pipeline.
This is extremely readable and reduces bugs.
Example: compute total spend per customer, then select top customers.
;WITH CustomerSpend AS
(
SELECT
soh.CustomerID,
SUM(soh.TotalDue) AS TotalSpent,
COUNT(*) AS OrderCount
FROM Sales.SalesOrderHeader AS soh
GROUP BY soh.CustomerID
),
TopCustomers AS
(
SELECT
cs.CustomerID,
cs.TotalSpent,
cs.OrderCount
FROM CustomerSpend AS cs
WHERE cs.TotalSpent >= 50000.00
)
SELECT TOP (25)
tc.CustomerID,
tc.OrderCount,
tc.TotalSpent
FROM TopCustomers AS tc
ORDER BY tc.TotalSpent DESC;
DBA habit: name your CTEs by what they represent (CustomerSpend, TopCustomers), not vague names like CTE1.
Job-ready takeaway: CTEs are best for clarity inside a single statement.
Temp tables are best for multi-step scripts and performance tuning control.
DBAs frequently need to de-duplicate results. The cleanest pattern is:
You will learn window functions next lesson; for now, focus on the CTE structure.
;WITH RankedOrders AS
(
SELECT
soh.CustomerID,
soh.SalesOrderID,
soh.OrderDate,
soh.TotalDue,
ROW_NUMBER() OVER (PARTITION BY soh.CustomerID ORDER BY soh.OrderDate DESC) AS rn
FROM Sales.SalesOrderHeader AS soh
)
SELECT
ro.CustomerID,
ro.SalesOrderID,
ro.OrderDate,
ro.TotalDue
FROM RankedOrders AS ro
WHERE ro.rn = 1
ORDER BY ro.OrderDate DESC;
What this does: returns the most recent order per customer.
This is an extremely common reporting requirement.
A recursive CTE is used for hierarchical relationships:
employee → manager, category → parent category, organizational trees.
In many DBA environments, recursion is used carefully because large hierarchies can be expensive.
You should still understand the pattern because you will see it in real systems.
This is a safe template. You must have:
/*
Template:
;WITH CTE AS
(
-- Anchor member
SELECT ...
UNION ALL
-- Recursive member
SELECT ...
FROM SomeTable t
JOIN CTE ON ...
)
SELECT * FROM CTE;
*/
DBA safety note: SQL Server has a recursion limit (default 100).
You can control it with OPTION (MAXRECURSION n).
If the previous statement didn’t end with a semicolon, SQL Server can throw a syntax error.
Fix by using ;WITH.
CTE scope is only the next statement. If you need reuse across multiple statements, use a temp table.
A CTE can make queries readable, but it can also hide mistakes if you don’t validate row counts.
Always confirm output grain.
SQL Server may inline CTE logic into the final query. Do not assume the CTE is a separate stored result.
Always verify with the execution plan (later section).
Do these in AdventureWorks. Save each script in your “DBA script pack.”
RecentOrders that returns the last 14 days of orders, then filter to orders over $2,000.CustomerSpend (sum TotalDue by CustomerID), then TopCustomersNext, you will learn window functions—one of the most valuable skills for reporting, troubleshooting,
and “top per group” logic. Window functions are a major reason CTEs become so powerful in DBA scripts.
Not a member yet? Register now
Are you a member? Login now