CTE (Common Table Expression) & Recursive CTE
CTE (Common Table Expression) & Recursive CTE
CTEs are table expressions, meaning they return a temporary result that can be
used in the scope of an SELECT, INSERT, UPDATE, DELETE, or APPLY statement.
WITH <CTE_name>
AS
(
<inner_query>
)
<outer_query>;
Example
;WITH C AS
(
SELECT
ROW_NUMBER() OVER(PARTITION BY categoryid
ORDER BY unitprice, productid) AS rownum,
categoryid, productid, productname, unitprice
FROM Production.Products
)
SELECT categoryid, productid, productname, unitprice
FROM C
WHERE rownum <= 2;
• Notice that when we define the CTE we give the result a name as well
its columns. In this way a CTE acts like a VIEW.
WITH C1 AS
(
SELECT ...
FROM T1
WHERE ...
),
C2
(
SELECT
FROM C1
WHERE ...
)
SELECT ...
FROM C2
WHERE ...;
Recursive CTE
• Recursive CTEs are use repeated procedural loops.
• The recursive query call themselves until the query satisfied the
condition. In a recursive CTE we should provide a where condition to
terminate the recursion.:
• We will see how to create a simple Recursive query to display the Row
Number from 1 to 10 using a CTE.
Example
Declare @RowNo int =1;
;with ROWCTE as
(
SELECT @RowNo as ROWNO
UNION ALL
SELECT ROWNO+1
FROM ROWCTE
WHERE RowNo < 10
)
SELECT * FROM ROWCTE
• A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE statement
that references some or all the CTE columns.
• Specifying more than one WITH clause in a CTE is not allowed. For example, if
a CTE_query_definition contains a subquery, that subquery cannot contain a nested
WITH clause that defines another CTE.
• When a CTE is used in a statement that is part of a batch, the statement before it must be
followed by a semicolon.
CTE versus Derived Table
• Derived tables are table results defined in the FROM clause. Given that derived tables return a table
expression, it should be no surprise that you can use CTEs in their place.
SELECT Quota.TerritoryID,
Quota.TerritoryQuota,
Sales.TerritorySales,
Sales.TerritorySales - Quota.TerritoryQuota
FROM (SELECT TerritoryID,
SUM(SalesQuota) AS TerritoryQuota
FROM Sales.SalesPerson
GROUP BY TerritoryID) AS Quota
INNER JOIN
(SELECT SOH.TerritoryID,
SUM(SOH.TotalDue) AS TerritorySales
FROM Sales.SalesOrderHeader AS SOH
GROUP BY SOH.TerritoryID) AS Sales
ON Quota.TerritoryID = Sales.TerritoryID
Using CTEs instead of derived tables
;WITH Quota (territoryid, quota)
AS (SELECT territoryid,
Sum(salesquota) AS TerritoryQuota
FROM sales.salesperson
GROUP BY territoryid),