Search…
Common Table Expressions (i.e. CTEs)
Common Table Expressions (CTEs) are powerful SQL concept that allow you to create re-usable temporal result sets, which can be referenced as a table within your SQL. CTEs are available in many common database engines and are available in latest versions of all of the support grammars.
CTEs come in two basic types:
  • Non-recursive — These are statements that do not reference themselves, in simplified terms they are like a derived table that can be referenced by a user-defined name.
  • Recursive — Recursive CTEs reference themselves and are generally used for creating hierarchical data—such as creating a parent/child relationship within a table.
While all of the grammars currently support CTEs, there is enough difference between the various databases implementations of CTEs that unless your CTEs are fairly basic, using CTEs within your project will most likely tie your project to a specific database, unless you account for the differences in your code.
However, CTEs are can be extremely useful to solve certain use cases.
To add CTEs to your queries, you have two methods available:
  • with() — Allows you to define a non-recursive CTE.
  • withRecursive() — Allows you to define a recursive CTE.
Some database engines require the recursive keyword anytime at least one of your CTEs is recursive, but some database engines (e.g. SQL Server and Oracle) do not require the keyword. qb will manage adding the keyword, if necessary. If your query does use recursion you should use the withRecursive()method to avoid issues when migrating grammars.

with

Name
Type
Required
Default
Description
name
string
true
The name of the CTE.
input
QueryBuilder | Function
true
Either a QueryBuilder instance or a function to define the derived query.
columns
Array<String>
false
[]
An optional array containing the columns to include in the CTE.
recursive
boolean
false
false
Determines if the CTE statement should be a recursive CTE. Passing this as an argument is discouraged. Use the dedicated withRecursive where possible.
You can build a CTE using a function:
QueryBuilder
1
// qb
2
query.with( "UserCTE", function ( q ) {
3
q
4
.select( [ "fName as firstName", "lName as lastName" ] )
5
.from( "users" )
6
.where( "disabled", 0 );
7
} )
8
.from( "UserCTE" )
9
.get();
Copied!
MySQL
1
WITH `UserCTE` AS (
2
SELECT
3
`fName` as `firstName`,
4
`lName` as `lastName`
5
FROM `users`
6
WHERE `disabled` = 0
7
) SELECT * FROM `UserCTE`
Copied!
Alternatively, you can use a QueryBuilder instance instead of a function:
QueryBuilder
1
// qb
2
var cte = query
3
.select( [ "fName as firstName", "lName as lastName" ] )
4
.from( "users" )
5
.where( "disabled", 0 );
6
7
query.with( "UserCTE", cte )
8
.from( "UserCTE" )
9
.get();
Copied!
MySQL
1
WITH `UserCTE` AS (
2
SELECT
3
`fName` as `firstName`,
4
`lName` as `lastName`
5
FROM `users`
6
WHERE `disabled` = 0
7
)
8
SELECT * FROM `UserCTE`
Copied!
A single query can reference multiple CTEs:
QueryBuilder
1
query.with( "UserCTE", function ( q ) {
2
q.select( [ "id", "fName as firstName", "lName as lastName" ] )
3
.from( "users" )
4
.where( "disabled", 0 );
5
} )
6
.with( "BlogCTE", function ( q ) {
7
q.from( "blogs" )
8
.where( "disabled", 0 );
9
} )
10
.from( "BlogCTE as b" )
11
.join( "UserCTE as u", "b.Creator", "u.id" )
12
.get();
Copied!
MySQL
1
WITH `UserCTE` AS (
2
SELECT
3
`id`,
4
`fName` as `firstName`,
5
`lName` as `lastName`
6
FROM `users`
7
WHERE `disabled` = 0
8
),
9
`BlogCTE` AS (
10
SELECT *
11
FROM `blogs`
12
WHERE `disabled` = 0
13
)
14
SELECT *
15
FROM `BlogCTE` AS `b`
16
INNER JOIN `UserCTE` AS `u`
17
ON `b`.`Creator` = `u`.`id`
Copied!

withRecursive

Name
Type
Required
Default
Description
name
string
true
The name of the CTE.
input
QueryBuilder | Function
true
Either a QueryBuilder instance or a function to define the derived query.
columns
Array<String>
false
[]
An optional array containing the columns to include in the CTE.
IMPORTANT — The way the SQL in a recursive CTEs are written, using them in your code is likely to lock in you in to a specific database engine, unless you structure your code to build the correct SQL based on the current grammar being used.
Here is an example of building a recursive CTE using SQL Server which would return all parent/child rows and show their generation/level depth:
QueryBuilder
1
query
2
.withRecursive( "Hierarchy", function ( q ) {
3
q.select( [ "Id", "ParentId", "Name", q.raw( "0 AS [Generation]" ) ] )
4
.from( "Sample" )
5
.whereNull( "ParentId" )
6
// use recursion to join the child rows to their parents
7
.unionAll( function ( q ) {
8
q.select( [
9
"child.Id",
10
"child.ParentId",
11
"child.Name",
12
q.raw( "[parent].[Generation] + 1" )
13
] )
14
.from( "Sample as child" )
15
.join( "Hierarchy as parent", "child.ParentId", "parent.Id" );
16
} );
17
}, [ "Id", "ParentId", "Name", "Generation" ] )
18
.from( "Hierarchy" )
19
.get();
Copied!
SqlServer
1
WITH [Hierarchy] ([Id], [ParentId], [Name], [Generation]) AS (
2
SELECT
3
[Id],
4
[ParentId],
5
[Name],
6
0 AS [Generation]
7
FROM [Sample]
8
WHERE [ParentId] IS NULL
9
UNION ALL
10
SELECT
11
[child].[Id],
12
[child].[ParentId],
13
[child].[Name],
14
15
[parent].[Generation] + 1
16
FROM [Sample] AS [child]
17
INNER JOIN [Hierarchy] AS [parent]
18
ON [child].[ParentId] = [parent].[Id]
19
) SELECT * FROM [Hierarchy]
Copied!
Last modified 1yr ago
Copy link
Edit on GitHub