start with ... connect by

jss001發表於2009-02-24
start with ... connect by用法簡介 sql有向圖問題期待新解決方案

/*******************************************************************************

透過START WITH . . . CONNECT BY . . .子句來實現SQL的層次查詢.
自從Oracle 9i開始,可以透過 SYS_CONNECT_BY_PATH 函式實現將父節點到當前行內容以“path”或者層次元素列表的形式顯示出來。

自從Oracle 10g 中,還有其他更多關於層次查詢的新特性 。例如,有的時候使用者更關心的是每個層次分支中等級最低的內容。
那麼你就可以利用偽列函式CONNECT_BY_ISLEAF來判斷當前行是不是葉子。如果是葉子突嵩諼繃兄邢允盡?”,
如果不是葉子而是一個分支(例如當前內容是其他行的父親)就顯示“0”。

在Oracle 10g 之前的版本中,如果在你的樹中出現了環狀迴圈(如一個孩子節點引用一個父親節點),
Oracle 就會報出一個錯誤提示:“ ORA-01436: CONNECT BY loop in user data”。如果不刪掉對父親的引用就無法執行查詢操作。
而在 Oracle 10g 中,只要指定“NOCYCLE”就可以進行任意的查詢操作。與這個關鍵字相關的還有一個偽列——CONNECT_BY_ISCYCLE,
如果在當前行中引用了某個父親節點的內容並在樹中出現了迴圈,那麼該行的偽列中就會顯示“1”,否則就顯示“0”。

The start with .. connect by clause can be used to select data that has a hierarchical relationship
(usually some sort of parent->child, boss->employee or thing->parts).
It is also being used when an sql execution plan is explained.

syntax:
select ... [start with initial-condition] connect by [nocycle] recurse-condition

level
With level it is possible to show the level in the hierarchical relation of all the data.

--oracle 9i
sys_connect_by_path
With sys_connect_by_path it is possible to show the entire path from the top level down to the 'actual' child.

--oracle 10g
connect_by_root
connect_by_root is a new operator that comes with Oracle 10g and enhances the ability to perform hierarchical queries.
connect_by_is_leaf
connect_by_isleaf is a new operator that comes with Oracle 10g and enhances the ability to perform hierarchical queries.
connect_by_iscycle
connect_by_is_cycle is a new operator that comes with Oracle 10g and enhances the ability to perform hierarchical queries.

--start with ... connect by ... 的處理機制
How must a start with ... connect by select statement be read and interpreted?
If Oracle encounters such an SQL statement, it proceeds as described in the following pseude code.

for rec in (select * from some_table) loop
if FULLFILLS_START_WITH_CONDITION(rec) then
RECURSE(rec, rec.child);
end if;
end loop;

procedure RECURSE (rec in MATCHES_SELECT_STMT, new_parent IN field_type) is
begin
APPEND_RESULT_LIST(rec);
for rec_recurse in (select * from some_table) loop
if FULLFILLS_CONNECT_BY_CONDITION(rec_recurse.child, new_parent) then
RECURSE(rec_recurse,rec_recurse.child);
end if;
end loop;
end procedure RECURSE;

created by zhouwf0726 2006.

*******************************************************************************/

--建立測試表,增加測試資料

create table test(superid varchar2(20),id varchar2(20));

insert into test values('0','1');
insert into test values('0','2');

insert into test values('1','11');
insert into test values('1','12');

insert into test values('2','21');
insert into test values('2','22');

insert into test values('11','111');
insert into test values('11','112');

insert into test values('12','121');
insert into test values('12','122');

insert into test values('21','211');
insert into test values('21','212');

insert into test values('22','221');
insert into test values('22','222');

commit;

--層次查詢示例
select level||'層',connect_by_root,lpad(' ',level*5)||id id
from test
start with superid = '0' connect by prior id=superid;

select level||'層',connect_by_isleaf,lpad(' ',level*5)||id id
from test
start with superid = '0' connect by prior id=superid;

--給出兩個以前在"資料庫字串分組相加之四"中的例子來理解start with ... connect by ...
--功能:實現按照superid分組,把id用";"連線起來
--實現:以下兩個例子都是透過構造2個偽列來實現connect by連線的。

/*------method one------*/
select superid,ltrim(max(sys_connect_by_path(id,';')),';') from(
select superid,id,row_number() over(partition by superid order by superid) id1,
row_number() over(order by superid) + dense_rank() over(order by superid) id2
from test
)
start with id1=1 connect by prior id2 = id2 -1
group by superid order by superid;

/*------method two------*/
select distinct superid,ltrim(first_value(id) over(partition by superid order by l desc),';')
from(
select superid,level l,sys_connect_by_path(id,';') id
from(
select superid,id,superid||rownum parent_rn,superid||to_char(rownum-1) rn
from test
)
connect by prior parent_rn = rn
);

--下面的例子實現把一個整數的各個位上的數字相加,透過這個例子我們再次理解connect by.

create or replace function f_digit_add(innum integer) return number
is
outnum integer;
begin
if innum<0 then
return 0;
end if;
select sum(nm) into outnum from(
select substr(innum,rownum,1) nm from dual connect by rownum);
return outnum;
end f_digit_add;
/

select f_digit_add(123456) from dual;

[@more@]

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/18921899/viewspace-1016967/,如需轉載,請註明出處,否則將追究法律責任。

相關文章