CTE is named expression that holds result of Sub-select temporarily. The main advantage of CTE is light-weight and recursion; hence it is ultimate choice for navigating hierarchical data.
1. Overview of CTE
1.1. Aspects of CTE
- A CTE is essentially a disposable view, and can be used in place of derived tables.
- CTE is defined by WITH-Keyword, and it is best practice to begin CTE with Semi-column, since CTE is not single-statement it is batch of statement.
- CTE can be used in SELECT, INSERT, UPDATE or DELETE statement. But SELECT is desired.
- CTE is single-time visible to sub-sequent query only.
- CTE can be used to define VIEWS as part of View’s SELECT query.
- CTE can be defined as Recursive-CTE or Non-Recursive-CTE;
- Recursive-CTE calls itself whereas Non-Recursive does not. It is best practice to use MAXRECURSION to prevent infinite loops in Recursive-CTE.
- CTE (Recursive) mainly used for navigating hierarchical data.
- CTE (Non-Recursive) is substitutable to derived-table (or Inline-view or Sub-Query); Since CTE and Derived Table are non-persistent and have same visibility.
- CTE is not substitutable to Temp-Table or Table-Variable; Because CTE and TempTable are differ based on: Visibility and Persistency; CTE visibility is limited to only a single SQL statement that would very first to it; whereas Temp-Table or Table-Variable is visible to at-least current scope where they defined and Temp-Table are persistent to Db.
- CTE can be defined in SUB-ROUTIENS such as user-defined functions, stored procedures triggers or views.
- You can define cursor on CTE-referenced Select Query.
1.2. Advantages of CTE
Main advantage of CTE is light-weight and Recursion. The other advantages are: Readability and maintenance;- Light-weight: CTEs are light-weight than Temporary-tables (or Table-Variable). Since, they are non-persistent and do not write any schema information or business-data to disk. The second thing is that CTEs get cleaned up automatically by SQL Server. Therefore, CTE does not create any maintenance over-head and no performance impact.
- Recursion: A recursive-CTE is ultimate for navigating hierarchical data;
- Readability: CTE improves readability; this is awesome aspect that developers always look for. CTE separates data extraction from your main query. Consider a complex query containing multiple joins, data filter operations, group by etc. Using CTE, you can put all JOIN-operations in one CTE-variable; and all filters in another CTE variable then conveniently access them in your query. This will rapidly increase readability of your query. Hence, CTE is substitutable to Derived Table.
1.3 Limitations of CTE
- CTE-Select cannot include following clauses:
- DISTINCT
- INTO
- ORDER BY (except when a TOP clause is specified)
- GROUP BY
- OPTION clause with query hints
- FOR BROWSE
- CTE-cannot include PIVOTed or Un-PIVOTed data;
2.3. Main Usages by Examples:
- Extracting Hierarchical data (see Recursive-CTE)
- Self-joining Sub-query result-set (see last-example)
- Increasing readability for complex query
2. Working with Common-Table-Expression (CTE)
2.1. Syntax:
WITH expression_name [ ( column_name[,...n] ) ]
AS ( CTE_query_definition )
Where
AS ( CTE_query_definition )
- expression_name = The name of the common table expression.
- column_name [ ,...n ] = The unique column names of the expression.
- CTE_query_definition = The SELECT query that defines the common table expression.
2.2. Examples:
Simple-CTE: Defining a table expression with WITH statement.
;WITH MySearch (ID, f_Name)
AS ( SELECT ID,first_Name FROM employee)
Select * from MySearchSimple-CTE2: Defining CTE without Column aliasing
;WITH MySearch
AS ( SELECT ID,first_Name FROM employee)
Select * from MySearchMultiple CTEs using single WITH AS statement.
;WITH A AS (select * from Table1 ),
B AS (select id, name Table2),
C AS (Select name from Table3)
Select * from A,B,C;CTE with SET-operators.
WITH cte_Sample1
AS (SELECT name from TABLE1 UNION ALL SELECT name from TABLE2)
Select * from cte_Sample1;-
Implementing self join to derived table using CTE.
WITH Temp AS (SELECT * FROM Table1)
SELECT t1.*, t2.*
FROM Temp t1 INNER JOIN Temp t2 ON t1.ID = t2.ID;
3. Recursive CTE - Basic understanding
- Recursive-CTE allows you to write hierarchical queries.
- Recursive-CTE referenced by itself;
- Recursive-CTE executes in BFS(Breadth-First-Search) order. For DEF(Depth-First-Search) order make anchor(outer) query ORDER BY a path;
- Recursive-CTE works as cursor in first iteration AnchorQuery executed and in sub-sequent iteration RecursiveQuery executed.
3.1. Syntax- Recursive-CTE
;WITH cte_name ( Column_name [,...n] )
AS (
Select Query - - Anchor member is defined
UNION ALL
Select Query referencing cte_name - - Recursive member
)
Outer_Query [OPTION (MAXRECURSION N) ]
Where:
AS (
Select Query - - Anchor member is defined
UNION ALL
Select Query referencing cte_name - - Recursive member
)
Outer_Query [OPTION (MAXRECURSION N) ]
- Anchor Select Query is executed only once and it holds result of recursion. But Recursive Select query executed multiple times repeatedly.
- The FROM-Clause of Recursive-Select must refer CTE at most once.
- The Anchor-Select and Recursive-Select should combined together by UNION-ALL
- The no. of and type of columns in Anchor-Select and Recursive-Select must be same.
- Use MAXRECURSION to prevent infinite loop in Recursive-CTE
3.2. Examples
-
Simple-Recursive-CTE with single anchor query
WITH MyCTE(x)
as
(
Select x = 'hello'
UNION ALL
Select x + 'a' from MyCTE where len(x) < 10
)
select x from MyCTE
order by x;
OUTPUT :
hello
helloa
helloaa
helloaaa
helloaaaa
helloaaaaa
-
Simple Recursive-CTE: Multiple Anchor Queries
with MyCTE(x)
as
(
select x = 'hello'
union all
select x = 'goodbye'
union all
select x + 'a' from MyCTE
where len(x) < 10
)
select x from MyCTE
order by len(x), x
OUTPUT :
hello
helloa
goodbye
helloaa
goodbyea
helloaaa
goodbyeaa
helloaaaa
goodbyeaaa
helloaaaaa -
Multiple-Recrusive Queries in CTE
WITH MyCTE(x)
As
( Select x = 'hello'
UNION ALL
Select x + 'a' from MyCTE where len(x) < 10
UNION All Select x + 'b' from MyCTE where len(x) < 10
)
Select x
From MyCTE
Order by len(x), x
OUTPUT
hello
helloa
hellob
helloaa
helloab
helloba
hellobb
helloaaa
helloaab
helloaba
helloabb
hellobaa
hellobab
hellobba
hellobbb
helloaaaa
….
63 rows of output.
4. Hirarchical Data Processing using Recursive-CTE
Let us consider a self-reference Table: Persons4.1: Providing Hierarchical Context to Table
WITH cte_Person (Id, Name, Designation, Manager, [Level] , [Root] )
AS
(
SELECT Id, Name, Designation, Manager, 0, Id
FROM Person
WHERE Manager IS NULL
UNION ALL
SELECT p.Id, p.Name, p.Designation, p.Manager, [Level] + 1, [Root]
FROM Person p
INNER JOIN cte_Person cte_p ON p.Manager = cte_p.Id
)
SELECT * FROM cte_Person;
AS
(
SELECT Id, Name, Designation, Manager, 0, Id
FROM Person
WHERE Manager IS NULL
UNION ALL
SELECT p.Id, p.Name, p.Designation, p.Manager, [Level] + 1, [Root]
FROM Person p
INNER JOIN cte_Person cte_p ON p.Manager = cte_p.Id
)
SELECT * FROM cte_Person;
Where [Level] and [Root] are derived columns not avialable in Table and evaluated by Recrusive-CTE;
Pictorial representation of above Hierarchical data
4.2: Querying Over hierarhical data
-
Find all sub-ordinates of ITManager-A directly or indirectly (i.e., C D G I J K L M N O P)
WITH cte_Person AS (………do as above ………….)
SELECT Name FROM cte_Person
WHERE ([Root] = N'1') AND (Manager IS NOT NULL) -
Find all sub-ordinates of FinanceManager(under E i.e. H)
WITH cte_Person AS (………do as above ………….)
SELECT Name FROM cte_Person
WHERE ([Root] = N'2') AND (Manager IS NOT NULL) -
Find all persons working under Asst.ITManager(Under G => I J K L M N O P )
WITH cte_Person AS (………do as above ………….)
SELECT Name FROM cte_Person
WHERE ([Root] = N'1') AND ([Level] > 2)
For the sake of siplicity;
- We omit CTE definition in examples; Just copy it from above example and apply or you can define CTE in separate View and access in your query.
- In WHERE-CLAUSE we use constant value as [Root]=1, instead use sub-query to get root value;
5. Conclusion
CTE is a new feature introduced in SQL Server 2005. It allows you to hold the result-set temporarily for immediate Sql Query. CTE is replicable with Derived table or inline-view. The beauty of CTE is that it is non-persistent and auto-disposable object; it does not write any schema information or user data to disk. The other awesome advantage of CTE is that recursion; you can use recursive-CTE to navigate hierarchical data reliability. In my opinion, CTE is desirable when- You need to hold result set temporarily for a single query only.
- Your query is large and contains multiple joins.
- You need to navigate the hierarchical data;
- You need to Self-Join the Sub-query data;
Your blog is in a convincing manner, thanks for sharing such an information with lots of your effort and time sql server dba online training and career consultancy in Noida
ReplyDelete