Recursive common table expressions (CTE) for arbitrary depth hierarchies
- Posted by Michael MacDonald
- on Aug, 30, 2017
- in SQL
- Blog No Comments.
The challenge of storing an arbitrary depth hierarchy isn’t so much with the storing of it as it is with the querying it and rendering it in a useful fashion.
Given a simple hierarchy stored in a table that has a self-referential parent_id such as:
create table hierarchy ( id integer NOT NULL, parent_id integer, name varchar, CONSTRAINT pk1 PRIMARY KEY (id), CONSTRAINT fk_self FOREIGN KEY (parent_id) REFERENCES hierarchy (id) );
With some sample data:
insert into hierarchy (id,parent_id,name) values (1,null,'A') ,(2,1,'B') ,(3,1,'C') ,(4,1,'D') ,(5,2,'E') ,(6,2,'F') ,(7,6,'G') ,(8,6,'H') ,(9,7,'I') ,(10,7,'J') ,(11,4,'K') ,(12,4,'L') ,(13,11,'M') ;
We can extract the data:
with recursive hierarchy_cte (id,parent_id,name, lvl,lineage) as ( /* base query */ select id , parent_id , name , 0 lvl , name lineage from hierarchy where parent_id is null union all /* recursing query */ select h.id , h.parent_id , h.name , lvl+1 , concat(lineage, ' ', h.name) lineage from hierarchy h inner join hierarchy_cte c on h.parent_id=c.id ) /* reporting query */ select concat(repeat('-',lvl * 4),Name) Tree ,hc.* from hierarchy_cte hc order by lineage , name
Which yields the following results:
tree | id | parent_id | name | lvl | lineage |
---|---|---|---|---|---|
A | 1 | (null) | A | 0 | A |
—-B | 2 | 1 | B | 1 | A B |
——–E | 5 | 2 | E | 2 | A B E |
——–F | 6 | 2 | F | 2 | A B F |
————G | 7 | 6 | G | 3 | A B F G |
—————-I | 9 | 7 | I | 4 | A B F G I |
—————-J | 10 | 7 | J | 4 | A B F G J |
————H | 8 | 6 | H | 3 | A B F H |
—-C | 3 | 1 | C | 1 | A C |
—-D | 4 | 1 | D | 1 | A D |
——–K | 11 | 4 | K | 2 | A D K |
————M | 13 | 11 | M | 3 | A D K M |
——–L | 12 | 4 | L | 2 | A D L |
Related
Comments & Responses
Recent Posts
- The shape of structured data
- Writing very small executable
- The challenge of type asserting []interface{} to string slices.
- Serializing/Deserializing a complex GO struct to store externally (i.e. in a database, redis, or text file)
- Fun with golang, Google Maps api, and proxying requests