Common table expression
WITH [RECURSIVE] cte-table-name AS ( select-stmt )[,...] primary-select-stmt
What are Common Table Expressions?
Common table expressions ("CTEs") act like temporary views that exist only for the duration of a single ECSQL statement. There are two types of CTE:
Ordinary Common Table Expressions
This is mainly used to factor out subqueries, making the overall ECSQL statement easier to read and understand. It contains just a SELECT
statement with or without RECURSIVE
keyword.
WITH
el (Id, ClassId) AS (
SELECT ECInstanceId, ECClassId FROM bis.Element
) SELECT * FROM el;
Recursive Common Table Expressions
A recursive common table expression can be used to walk a tree or graph. It is of the following form:
cte-table-name AS ( initial-select) UNION [ALL] recursive-select)
Here is a simple example of how we can write a CTE. In the following query we want to generate a sequence from 1 through 5. We start with an initial value of x = 1 and then recursively do x+1 until the value of x is less then 6.
WITH RECURSIVE
cnt (x) AS (
SELECT 1
UNION ALL
SELECT x+1 FROM cnt WHERE x<6
)
SELECT * from cnt;
-- output
x
------
1
2
3
4
5
6
As another example, we might want to traverse a class hierarchy starting from a base class down to all derived classes, generating a row for each class. Each row should could contain 2 columns: the depth of the derived class relative to the base class and a path string describing its relationship to the base class. Using BisCore:GeometricElement2d
as the base class produces the following ECSQL and resultant output:
WITH RECURSIVE
base_classes (aId, aParentId, aPath, aDepth) AS (
SELECT c.ECInstanceId, NULL, c.Name, 0 FROM meta.ECClassDef c WHERE c.Name='GeometricElement2d'
UNION ALL
SELECT c.ECInstanceId, cbc.TargetECInstanceId, aPath || '/' || c.Name, aDepth + 1
FROM meta.ECClassDef c
JOIN meta.ClassHasBaseClasses cbc ON cbc.SourceECInstanceId = c.ECInstanceId
JOIN base_classes ON aId = cbc.TargetECInstanceId
)
SELECT bc.aDepth depth, bc.aPath FROM base_classes bc
JOIN meta.ECClassDef a ON a.ECInstanceId= bc.aId
JOIN meta.ECClassDef b ON b.ECInstanceId= bc.aParentId;;
-- output
depth | aPath
---------------------------------------
1 | GeometricElement2d/GraphicalElement2d
2 | GeometricElement2d/GraphicalElement2d/AnnotationElement2d
2 | GeometricElement2d/GraphicalElement2d/DrawingGraphic
2 | GeometricElement2d/GraphicalElement2d/ViewAttachment
2 | GeometricElement2d/GraphicalElement2d/DetailingSymbol
3 | GeometricElement2d/GraphicalElement2d/AnnotationElement2d/TextAnnotation2d
3 | GeometricElement2d/GraphicalElement2d/DrawingGraphic/SheetBorder
3 | GeometricElement2d/GraphicalElement2d/DetailingSymbol/Callout
3 | GeometricElement2d/GraphicalElement2d/DetailingSymbol/TitleText
3 | GeometricElement2d/GraphicalElement2d/DetailingSymbol/ViewAttachmentLabel
4 | GeometricElement2d/GraphicalElement2d/DetailingSymbol/Callout/DetailCallout
4 | GeometricElement2d/GraphicalElement2d/DetailingSymbol/Callout/ElevationCallout
4 | GeometricElement2d/GraphicalElement2d/DetailingSymbol/Callout/PlanCallout
4 | GeometricElement2d/GraphicalElement2d/DetailingSymbol/Callout/SectionCalloutt
Last Updated: 02 February, 2022