![]() | |
#1
| |||
| |||
|
#2
| |||
| |||
|
|
Hi Guys, Can anyone shred some light here? Sample DDL Code: create table DEPARTMENT ( DEP_ID integer not null primary key, NAME VARCHAR(32) not null, PARENT integer not null ) ; insert into DEPARTMENT VALUES (1,'A',-2),(2,'B',1),(3,'C',2), (4,'D',-2),(5,'E',4),(6,'F',5),(7,'G',6); create table PERSON ( PERSON_ID integer not null primary key, NAME VARCHAR(128) not null, DEP_ID integer, constraint FK_01 foreign key (DEP_ID) references DEPARTMENT(DEP_ID) ); insert into PERSON values (1,'John',2),(2,'Mary',2),(3,'Suzan',3), (4,'Joe',3),(5,'Lewis',3),(6,'Elvis',4),(7,'Presle y',5),(8,'Preston', 5),(9,'Pee',7),(10,'Wee',7),(11,'Steve-o',7),(12,'Demi Moore',7); with t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 from DEPARTMENT union all select r.dep_id, d.parent, iter + 1 from t_recurs r, DEPARTMENT d where r.parent = d.dep_id and iter< 1000 ) select R.DEP_ID, COUNT(P.PERSON_ID) from T_RECURS R inner join DEPARTMENT D on (R.DEP_ID, R.PARENT) = (D.DEP_ID, D.PARENT) left outer join PERSON P on P.DEP_ID = D.DEP_ID group by R.DEP_ID ; The output looks good: DEP_ID DEP_COUNT ----------- ----------- 1 0 2 2 3 3 4 1 5 2 6 0 7 4 What am I actually looking for is also the count of Persons that are in all sub-departments in that hierarchy, including the departement a person is currently in. Somehow I can't get this to work. Expected output: DEP_ID DEP_COUNT HIERARCHY_COUNT ----------- ----------- --------------- 1 0 5 2 2 5 3 3 3 4 1 7 5 2 6 6 0 6 7 4 4 Any ideas? Thanks |
#3
| |||
| |||
|
|
with t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 from DEPARTMENT union all select r.dep_id, d.parent, iter + 1 from t_recurs r, DEPARTMENT d where r.parent = d.dep_id and iter< 1000 ) |
|
What am I actually looking for is also the count of Persons that are in all sub-departments in that hierarchy, including the departement a person is currently in. |

#4
| |||
| |||
|
|
Hi, I also would like to know if you are really using Oracle. As I don't have Oracle 11gR2 available (which is the first Oracle version to support recursive CTEs) I tested this using PostgreSQL. I expect this to run on Oracle 11gR2 as well. with t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 from DEPARTMENT union all select r.dep_id, d.parent, iter + 1 from t_recurs r, DEPARTMENT d where r.parent = d.dep_id and iter< 1000 ) That query is wrong, and that's why you need this "where iter < 1000" workaround. The basic recursion should be like this: with recursive t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 as iter from department where parent = -2 union all select d.dep_id, d.parent, iter + 1 from department d join t_recurs r on d.parent = r.dep_id ) select * from t_recurs; I changed two things: - the where condition for the first part of the union - selecting the d.dep_id instead of r.dep_id in the recursion part which makes the check for an infinite loop obsolete Now to your real question: What am I actually looking for is also the count of Persons that are in all sub-departments in that hierarchy, including the departement a person is currently in. this should do it as far as I can tell. with recursive t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 as iter from department where parent is null union all select d.dep_id, d.parent, iter + 1 from department d join t_recurs r on d.parent = r.dep_id ) select r.dep_id, (select count(*) from t_recurs r2 where r2.iter > r.iter) as department_count, (select count(*) from person where dep_id = any (select dep_id from t_recurs r3 where r3.iter >= r.iter)) as person_count from t_recurs r order by 1; Now, whether that will be fast enough is a completely different topic ![]() Thomas |
#5
| |||
| |||
|
|
I also would like to know if you are really using Oracle. |
|
with t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 from DEPARTMENT union all select r.dep_id, d.parent, iter + 1 from t_recurs r, DEPARTMENT d where r.parent = d.dep_id and iter< 1000 ) That query is wrong, and that's why you need this "where iter < 1000" workaround. |
|
this should do it as far as I can tell. with recursive t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 as iter from department where parent is null union all select d.dep_id, d.parent, iter + 1 from department d join t_recurs r on d.parent = r.dep_id ) select r.dep_id, (select count(*) from t_recurs r2 where r2.iter > r.iter) as department_count, (select count(*) from person where dep_id = any (select dep_id from t_recurs r3 where r3.iter >= r.iter)) as person_count from t_recurs r order by 1; |
#6
| ||||
| ||||
|
|
with t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 from DEPARTMENT union all select r.dep_id, d.parent, iter + 1 from t_recurs r, DEPARTMENT d where r.parent = d.dep_id and iter< 1000 ) That query is wrong, and that's why you need this "where iter< 1000" workaround. The recursion as works: you start from the bottom and work your way up. You will stop recurse when you reach the top level. However, he would need a WHERE NOT EXISTS to only start at the leaves. Now he starts on all nodes. |
|
with recursive t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 as iter from department where parent is null union all select d.dep_id, d.parent, iter + 1 from department d join t_recurs r on d.parent = r.dep_id ) select r.dep_id, (select count(*) from t_recurs r2 where r2.iter> r.iter) as department_count, (select count(*) from person where dep_id = any (select dep_id from t_recurs r3 where r3.iter>= r.iter)) as person_count from t_recurs r order by 1; I don't think this works. |

|
(And uses -2 rather than NULL to signify a root.) Yes, correct. Copy & Paste error. I just don't like "magic values" |
|
As for performance, on SQL Server it is certainly recommendable to to first save the result of the CTE into a temp table and work from there. Temp tables are (most of the time) a real performance killer in the Oracle world. |
#7
| |||
| |||
|
|
Erland Sommarskog wrote on 28.08.2010 11:36: with t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 from DEPARTMENT union all select r.dep_id, d.parent, iter + 1 from t_recurs r, DEPARTMENT d where r.parent = d.dep_id and iter< 1000 ) That query is wrong, and that's why you need this "where iter< 1000" workaround. The recursion as works: you start from the bottom and work your way up. You will stop recurse when you reach the top level. However, he would need a WHERE NOT EXISTS to only start at the leaves. Now he starts on all nodes. Hmm, for me the above query yields an endless "loop" when you remove the " iter < 1000" |
It works on PostgreSQL with the test data supplied ![]() |
|
(And uses -2 rather than NULL to signify a root.) Yes, correct. Copy & Paste error. I just don't like "magic values" ![]() |
#8
| |||
| |||
|
|
I noticed that in your own query you used the RECURSIVE keyword which is unknown to SQL Server. Maybe that is needed on PostgreSQL to prevent recursion from continuing? |
It works on PostgreSQL with the test data supplied ![]() Interesting. I certainly don't get the expected result on SQL Server. I don't have PostgresSQL, but it looks like a bug to me. If you try the extra test data I composed, does it still work? (See below.) |
It works on PostgreSQL with the test data supplied ![]() Interesting. I certainly don't get the expected result on SQL Server. I don't have PostgresSQL, but it looks like a bug to me. If you try the extra test data I composed, does it still work? (See below.) |

#9
| |||
| |||
|
|
Note that the OP incorrectly (at least in my eyes) selects the the dep_id from the t_recurs not from the department table, and I think that is causing the problem with the endless loop: with t_recurs (dep_id, parent, iter) as ( select dep_id, parent, 0 from DEPARTMENT union all select r.dep_id, <-- !!! this should be D.dep_id !!!! d.parent, iter + 1 from t_recurs r, DEPARTMENT d where r.parent = d.dep_id and iter < 1000 ) It should be d.dep_id (because r.dep_id will create a cycle). As soon as I select the dep_id from the department table in the second part of the union, the endless loop stops. |
#10
| |||
| |||
|
|
Having r.dep_id in the recursive part might be a mistake, but it should not cause infinite recursion. The recursion is controlled by the WHERE clause, and the WHERE does not involve r.dep_id. ... Not that I think the output is useful for the problem Michel presented, but I like to point out that there are other situations where this result is useful, at least if you remove all rows with PARENT = -2. To with, this table gives us a quick answer to "does subdept n belong dept m directly or indirectly". |
![]() |
| Thread Tools | |
| Display Modes | |
| |