Sql遞歸查詢
阿新 • • 發佈:2017-08-04
優化 value 內容 說明 其他 結構 菜單 等價 path
/*Sql遞歸查詢*/ /* 實際就是把所有樹的節點查找出來 Oracle的一個表中也可以保存樹形結構信息,用start with...connect by等關鍵字 eg:創建表並插入數據 */ Create table Tree(son char(10),father char(10)); insert into tree (SON, FATHER) values (‘孫子1‘, ‘兒子‘); insert into tree (SON, FATHER) values (‘孫子2‘, ‘兒子‘); insert into tree (SON, FATHER) values(‘兒子‘, ‘父親‘); insert into tree (SON, FATHER) values (‘父親‘, ‘祖父‘); /* son father 孫子1 兒子 孫子2 兒子 兒子 父親 父親 祖父 數據結構是: 祖父 | 父親 | 兒子 / 孫子1 孫子2*/ --查詢以祖父為根節點的所有節點值 SELECT son from Tree START WITH father=‘祖父‘ CONNECT BY PRIOR son=father /* START WITH後的內容可以看成一個WHERE的限制條件,其中START WITH 是指定樹的根 ,還可以指定多個根,比如father in (‘祖父‘,‘父親‘); CONNECTION BY PRIOR son=father相當於表明在遞歸過程中,查找到的樹種其他節點接著 又作為根結點,然後繼續遞歸。 只要記住 CONNECTION BY PRIOR那一行,要查詢的列名放在前,根列名放等號後*/ --例: --樹形菜單結構 CREATE TABLE tb_menu( id number(10) not null,--主鍵id title varchar(50),--標題 parent number(10) --父菜單id ); --父菜單數據 insert into tb_menu(id,title,parent) values(1,‘父菜單1‘,0); insert into tb_menu(id,title,parent) values(2,‘父菜單2‘,0); insert into tb_menu(id,title,parent) values(3,‘父菜單3‘,0); insert into tb_menu(id,title,parent) values(4,‘父菜單4‘,0); insert into tb_menu(id,title,parent) values(5,‘父菜單5‘,0); --一級子菜單 insert into tb_menu(id,title,parent) values(6,‘一級子菜單6_1‘,1); insert into tb_menu(id,title,parent) values(7,‘一級子菜單7_1‘,1); insert into tb_menu(id,title,parent) values(8,‘一級子菜單8_1‘,1); insert into tb_menu(id,title,parent) values(9,‘一級子菜單9_2‘,2); insert into tb_menu(id,title,parent) values(10,‘一級子菜單10_2‘,2); insert into tb_menu(id,title,parent) values(11,‘一級子菜單11_2‘,2); insert into tb_menu(id,title,parent) values(12,‘一級子菜單12_3‘,3); insert into tb_menu(id,title,parent) values(13,‘一級子菜單13_3‘,3); insert into tb_menu(id,title,parent) values(14,‘一級子菜單14_3‘,3); insert into tb_menu(id,title,parent) values(15,‘一級子菜單15_4‘,4); insert into tb_menu(id,title,parent) values(16,‘一級子菜單16_4‘,4); insert into tb_menu(id,title,parent) values(17,‘一級子菜單17_4‘,4); insert into tb_menu(id,title,parent) values(18,‘一級子菜單18_5‘,5); insert into tb_menu(id,title,parent) values(19,‘一級子菜單19_5‘,5); insert into tb_menu(id,title,parent) values(20,‘一級子菜單20_5‘,5); --二級子菜單 insert into tb_menu(id,title,parent) values(21,‘二級子菜單21_6‘,6); insert into tb_menu(id,title,parent) values(22,‘二級子菜單22_6‘,6); insert into tb_menu(id,title,parent) values(23,‘二級子菜單23_7‘,7); insert into tb_menu(id,title,parent) values(24,‘二級子菜單24_7‘,7); insert into tb_menu(id,title,parent) values(25,‘二級子菜單25_8‘,8); insert into tb_menu(id,title,parent) values(26,‘二級子菜單26_9‘,9); insert into tb_menu(id,title,parent) values(27,‘二級子菜單27_10‘,10); insert into tb_menu(id,title,parent) values(28,‘二級子菜單28_11‘,11); insert into tb_menu(id,title,parent) values(29,‘二級子菜單29_12‘,12); insert into tb_menu(id,title,parent) values(30,‘二級子菜單30_13‘,13); insert into tb_menu(id,title,parent) values(31,‘二級子菜單31_14‘,14); insert into tb_menu(id,title,parent) values(32,‘二級子菜單32_15‘,15); insert into tb_menu(id,title,parent) values(33,‘二級子菜單33_16‘,16); insert into tb_menu(id,title,parent) values(34,‘二級子菜單34_17‘,17); insert into tb_menu(id,title,parent) values(35,‘二級子菜單35_18‘,18); insert into tb_menu(id,title,parent) values(36,‘二級子菜單36_19‘,19); insert into tb_menu(id,title,parent) values(37,‘二級子菜單37_20‘,20); --三級子菜單 insert into tb_menu(id,title,parent) values(38,‘三級子菜單38_21‘,21); insert into tb_menu(id,title,parent) values(39,‘三級子菜單39_22‘,22); insert into tb_menu(id,title,parent) values(40,‘三級子菜單40_23‘,23); insert into tb_menu(id,title,parent) values(41,‘三級子菜單41_24‘,24); insert into tb_menu(id,title,parent) values(42,‘三級子菜單42_25‘,25); insert into tb_menu(id,title,parent) values(43,‘三級子菜單43_26‘,26); insert into tb_menu(id,title,parent) values(44,‘三級子菜單44_27‘,27); insert into tb_menu(id,title,parent) values(45,‘三級子菜單45_28‘,28); insert into tb_menu(id,title,parent) values(46,‘三級子菜單46_28‘,28); insert into tb_menu(id,title,parent) values(47,‘三級子菜單47_29‘,29); insert into tb_menu(id,title,parent) values(48,‘三級子菜單48_30‘,30); insert into tb_menu(id,title,parent) values(49,‘三級子菜單49_31‘,31); insert into tb_menu(id,title,parent) values(50,‘三級子菜單50_31‘,31); /* 數據結構: 父菜單1 | / 一級子菜單1_6 ... ... / 二級子菜單21_6... ... / 三級子菜單38_21... ... ... */ --①查詢表中所有父菜單 select * from tb_menu m where m.parent=0; --②查詢表中所有一級子菜單(即數據結構中的直屬子節點) select * from tb_menu m where m.parent=1; --③查詢父菜單1的所有子孫 select * from tb_menu m start with m.id=1 connect by PRIOR m.id=m.parent /* 這個SQL語句的等價寫法: select * from tb_menu m start with m.id=1 connect by m.parent=PRIOR m.id 可以記做: prior放在那裏,就找誰,即放在id前,即為找id的所有子孫後代 */ --④查詢一個節點的直屬父節點(用不到樹形查詢) select c.id, c.title, p.id as 父節點id , p.title as 父節點名稱 from tb_menu c, tb_menu p where c.parent=p.id and c.id=6 --⑤查詢一個節點的所有父節點,即父親節點,祖先節點(需要使用樹形查詢) select * from tb_menu m start with m.id=50 connect by PRIOR m.parent=m.id /*引入level和lpad實現分層顯示*/ select level, lpad(‘ ‘, 2 * level - 1) || m.id from tb_menu m start with m.id = 1 connect by prior m.id = m.parent /* prior就是表示上一條記錄的意思,即prior m.parent=m.id就是表示上一條記錄的父id是本條記錄的id 也就是本條記錄是上一條記錄的父親,那麽就是在查詢所有記錄的父節點/祖先節點,反之。 */ --⑥查詢一個節點(一級子菜單6_1)的兄弟節點(親兄弟) select * from tb_menu m where exists (select * from tb_menu m2 where m.parent=m2.parent and m2.id=6) /*從SQL語句理解就是:查詢表的數據,數據符合條件,在m2只要存在m.parent=m2.parent的數據,即取出*/ --⑦查詢一個節點所有同級的節點(族兄弟) with tmp as( select a.* ,level leaf from tb_menu a start with a.parent=0 connect by prior a.id=a.parent ) select * from tmp where leaf=(select leaf from tmp where id=50) /*在SQL語句⑦中使用了level來標識每個節點在表中的級別【level表示遞歸的層次,即查詢的深度】,使用了with語法來虛擬出了一張帶有級別的臨時表*/ /*=========================【補充】with語法:================================== 要求Oracle版本Oracle 9i及以上 其實就是把一些重復用到的SQL語句放在with as裏面,取一個別名,後面的查詢中就可以使用這個別名,對大批量的SQL 語句起到一個優化的作用。 eg1: with t1 as (select * from emp e where e.ename like ‘%N%‘) select * from t1; eg2: with t1 as (select * from emp), t2 as (select * from dept) select * from t1,t2 where t1.deptno=t2.deptno 對於union all通常使用with as提升效率 因為union all的每個執行部分可能相同,如果每個部分都執行一遍成本太高,如果使用with as短語只執行一次即可 eg3: with t1 as (select t.name as name from table1 t where t.age>20), t2 as (select s.lastname as name from table2 s where exists(select s.name from table3 p where s.id=p.id and p.id=‘a001‘)) select * from t1 union all select * from t2 =====================================================================*/ --⑧查詢一個節點的父節點的親兄弟節點 /* 1、如果當前節點為最頂級的節點,即level=1,沒有父節點,不考慮 2、如果當期節點為2級節點,即level=2,那麽level=1的節點就是他的父節點和父節點的兄弟節點 3、如果level是3或者3以上,那麽就要查詢出來其祖父節點,再判斷祖父節點的下一級(即父節點)是其父節點和父節點的兄弟節點 4、將結果集union在一起 */ with tmp as ( select m.*,level lev from tb_menu m start with m.parent=0 connect by m.parent= prior m.id ) select b.* from tmp b ,(select * from tmp where id=21 and lev=2) a where b.lev=1 union all select * from tmp where parent=( select distinct x.id from tmp x,--祖父 tmp y,--父親 (select * from tmp where id=21 and lev>2) z--兒子 where y.id=z.parent and x.id=y.parent ) --⑨查詢一個節點的父節點的所有兄弟節點(族兄弟) with tmp as ( select m.*,level leaf from tb_menu m start with m.parent=0 connect by m.parent= prior m.id ) select * from tmp where leaf=(select leaf from tmp where id=6)-1 --⑩列出全部路徑,比如省/市/區/街道/社區 /*從頂部開始*/ select sys_connect_by_path(title,‘/‘) from tb_menu m where id=50 start with parent =0 connect by parent =prior id /*從當前節點開始*/ select sys_connect_by_path(title,‘/‘) from tb_menu m start with id=50 connect by prior parent =id /* sys_connect_by_path函數是從start with開始的地方遍歷,並記錄下遍歷到的節點。start with開始的地方被視為根節點, 將遍歷到的路徑根據函數中的分隔符組成一個新的字符串! */ /* 還有一些功能同樣很強大的函數 connect_by_root函數用在列前,記錄的是當前節點的根節點的內容 select connect_by_root title,tb_menu.* from tb_menu start with id=50 connect by prior parent=id connect_by_isleaf函數用來判斷當前節點是否包含下級節點,如果包含下級節點說明不是葉子節點(即沒有子節點的節點,不管在第幾級,只要沒有子節點就是葉子節點),返回0, 否則不包含,返回1 select connect_by_isleaf,tb_menu.* from tb_menu start with parent=0 connect by parent =prior id; */
Sql遞歸查詢