create table emp ( ename varchar2(100), mgrname varchar2(100 );emp 表的每条记录通过指向上级mgrname的ename来标识。例如,假如JONES向KING报告,于是emp表中含有<ename='JONES', mgrname='KING'>的记录。假设,emp表也包含<ename='SCOTT', mgrname='JONES'>。此外,假如emp表不含有<ename='SCOTT',mgrname='KING'>记录,对于其它每对毗连的记录也是如此,那么它就是所谓的邻接表(Adjacency List)。如果正好相反,那emp表是可传递的闭包关系。
select 'TRUE' from tcemp where ename='SCOTT' and mgrname='KING'这个简便查询的牺牲代价 transitive closure maintenance .
with tcemp as (select ename,mgrname from tcemp union select tcemp.ename,emp.mgrname from tcemp, emp where tcemp.mgrname = emp.ename) select 'TRUE' from tcempwhere ename = 'SCOTT' and mgrname = 'KING';这个tcemp计算作为中间关联,或采用Oracle专有连接的语法:
select 'TRUE' from ( select ename from emp connect by prior mgrname = ename start with ename = 'SCOTT') where ename = 'KING';
ENAME | PATH |
KING | 1 |
JONES | 1.1 |
SCOTT | 1.1.1 |
ADAMS | 1.1.1.1 |
FORD | 1.1.2 |
SMITH | 1.1.2.1 |
BLAKE | 1.2 |
ALLEN | 1.2.1 |
WARD | 1.2.2 |
CLARK | 1.3 |
MILLER | 1.3.1 |
select e1.ename from emp e1, emp e2 where e2.path like e1.path || '%' and e2.ename='FORD'
select e1.ename from emp e1, emp e2 where e1.path like e2.path || '%' and e2.ename='JONES'
select e1.ename from emp e1, emp e2 where e2.path > e1.path and e2.path < e1.path || 'Z' and e2.ename='FORD'
select e1.ename from emp e1 where e2.path>e1.path and e2.path<e1.path || 'Z'
select e1.ename from emp where e1.path in ('1.1','1')这应该是个快速级联的执行方案。
select p2.emp from Personnel p1, Personnel p2where p1.lft between p2.lft and p2.rgtand p1.emp = 'Chuck'(注意:这个查询借自previously cited Celko article )
function x_numer( numer integer, denom integer ) RETURN integer IS ret_num integer; ret_den integer; BEGIN ret_num := numer+1; ret_den := denom*2; while floor(ret_num/2) = ret_num/2 loop ret_num := ret_num/2; ret_den := ret_den/2; end loop; RETURN ret_num; END; function x_denom( numer integer, denom integer ) ... RETURN ret_den; END;
function y_numer( numer integer, denom integer ) RETURN integer IS num integer; den integer; BEGIN num := x_numer(numer, denom); den := x_denom(numer, denom); while den < denom loop num := num*2; den := den*2; end loop; num := numer - num; while floor(num/2) = num/2 loop num := num/2; den := den/2; end loop; RETURN num; END; function y_denom( numer integer, denom integer ) ... RETURN den; END;
select x_number(39,32)||'/'||x_denom(39,32), y_number(39,32)||'/'||y_denom(39,32) from dual 5/8 19/32 select 5/8+19/32,39/32 from dual 1.21875 1.21875
function parent_numer( numer integer, denom integer ) RETURN integer IS ret_num integer; ret_den integer; BEGIN if numer=3 then return NULL; end if; ret_num := (numer-1)/2; ret_den := denom/2; while floor((ret_num-1)/4) = (ret_num-1)/4 loop ret_num := (ret_num+1)/2; ret_den := ret_den/2; end loop; RETURN ret_num; END; function parent_denom( numer integer, denom integer ) ... RETURN ret_den; END;
select parent_numer(27,32)||'/'||parent_denom(27,32) from dual 7/8
function sibling_number( numer integer, denom integer ) RETURN integer IS ret_num integer; ret_den integer; ret integer; BEGIN if numer=3 then return NULL; end if; ret_num := (numer-1)/2; ret_den := denom/2; ret := 1; while floor((ret_num-1)/4) = (ret_num-1)/4 loop if ret_num=1 and ret_den=1 then return ret; end if; ret_num := (ret_num+1)/2; ret_den := ret_den/2; ret := ret+1; end loop; RETURN ret; END;
select sibling_number(7,8) from dual; 1
function path( numer integer, denom integer ) RETURN varchar2 IS BEGIN if numer is NULL then return ''; end if; RETURN path(parent_numer(numer, denom), parent_denom(numer, denom)) || '.' || sibling_number(numer, denom); END; select path(15,16) from dual .2.1.1
function distance( num1 integer, den1 integer, num2 integer, den2 integer ) RETURN integer IS BEGIN if num1 is NULL then return -999999; end if; if num1=num2 and den1=den2 then return 0; end if; RETURN 1+distance(parent_numer(num1, den1), parent_denom(num1, den1), num2,den2); END; select distance(27,32,3,4) from dual 2
function child_numer ( num integer, den integer, child integer ) RETURN integer IS BEGIN RETURN num*power(2, child)+3-power(2, child); END; function child_denom ( num integer, den integer, child integer ) RETURN integer IS BEGIN RETURN den*power(2, child); END; select child_numer(3,2,3) || '/' || child_denom(3,2,3) from dual 19/16
function path_numer( path varchar2 ) RETURN integer IS num integer; den integer; postfix varchar2(1000); sibling varchar2(100); BEGIN num := 1; den := 1; postfix := '.' || path || '.'; while length(postfix) > 1 loop sibling := substr(postfix, 2, instr(postfix,'.',2)-2); postfix := substr(postfix, instr(postfix,'.',2), length(postfix) -instr(postfix,'.',2)+1); num := child_numer(num,den,to_number(sibling)); den := child_denom(num,den,to_number(sibling)); end loop; RETURN num; END; function path_denom( path varchar2 ) ... RETURN den; END; select path_numer('2.1.3') || '/' || path_denom('2.1.3') from dual 51/64
create table emps ( name varchar2(30), numer integer, denom integer ) alter table emps ADD CONSTRAINT uk_name UNIQUE (name) USING INDEX (CREATE UNIQUE INDEX name_idx on emps(name)) ADD CONSTRAINT UK_node UNIQUE (numer, denom) USING INDEX (CREATE UNIQUE INDEX node_idx on emps(numer, denom))
insert into emps values ('KING', path_numer('1'),path_denom('1')); insert into emps values ('JONES', path_numer('1.1'),path_denom('1.1')); insert into emps values ('SCOTT', path_numer('1.1.1'),path_denom('1.1.1')); insert into emps values ('ADAMS', path_numer('1.1.1.1'),path_denom('1.1.1.1')); insert into emps values ('FORD', path_numer('1.1.2'),path_denom('1.1.2')); insert into emps values ('SMITH', path_numer('1.1.2.1'),path_denom('1.1.2.1')); insert into emps values ('BLAKE', path_numer('1.2'),path_denom('1.2')); insert into emps values ('ALLEN', path_numer('1.2.1'),path_denom('1.2.1')); insert into emps values ('WARD', path_numer('1.2.2'),path_denom('1.2.2')); insert into emps values ('MARTIN', path_numer('1.2.3'),path_denom('1.2.3')); insert into emps values ('TURNER', path_numer('1.2.4'),path_denom('1.2.4')); insert into emps values ('CLARK', path_numer('1.3'),path_denom('1.3')); insert into emps values ('MILLER', path_numer('1.3.1'),path_denom('1.3.1')); commit;
create or replace view hierarchy as select name, numer, denom, y_numer(numer,denom) numer_left, y_denom(numer,denom) denom_left, x_numer(numer,denom) numer_right, x_denom(numer,denom) denom_right, path (numer,denom) path, distance(numer,denom,3,2) depth from emps
最后,我们创建一个分层报告
select lpad(' ',3*depth)||name from hierarchy order by numer_left/denom_left LPAD('',3*DEPTH)||NAME ----------------------------------------------- KING CLARK MILLER BLAKE TURNER MARTIN WARD ALLEN JONES FORD SMITH
select lpad(' ',3*depth)||name from hierarchy order by numer_right/denom_right desc LPAD('',3*DEPTH)||NAME ----------------------------------------------------- KING JONES SCOTT ADAMS FORD SMITH BLAKE ALLEN WARD MARTIN TURNER CLARK MILLER
select lpad(' ',3*depth)||name from hierarchy order by path LPAD('',3*DEPTH)||NAME ----------------------------------------------------- KING JONES SCOTT ADAMS FORD SMITH BLAKE ALLEN WARD MARTIN TURNER CLARK MILLER
select h1.name from hierarchy h1, hierarchy h2 where h2.name = 'JONES' and distance(h1.numer, h1.denom, h2.numer, h2.denom)>0; NAME ------------------------------ SCOTT ADAMS FORD SMITH
select h2.name from hierarchy h1, hierarchy h2 where h1.name = 'FORD' and distance(h1.numer, h1.denom, h2.numer, h2.denom)>0; NAME ------------------------------ KING JONES