日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ORACLE ROWNUM用法、select into与insert into区别、merge into的使用、递归查询

發布時間:2024/4/13 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ORACLE ROWNUM用法、select into与insert into区别、merge into的使用、递归查询 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

ROWNUM用法

ORACLE 中ROWNUM用法總結! 對于 Oracle 的 rownum 問題,很多資料都說不支持>,>=,=,between...and,只能用以上符號(<、<=、!=),并非說用>,& gt;=,=,between..and 時會提示SQL語法錯誤,而是經常是查不出一條記錄來,還會出現似乎是莫名其妙的結果來,其實您只要理解好了這個 rownum 偽列的意義就不應該感到驚奇,同樣是偽列,rownum 與 rowid 可有些不一樣,下面以例子說明假設某個表 t1(c1) 有 20 條記錄如果用 select rownum,c1 from t1 where rownum < 10, 只要是用小于號,查出來的結果很容易地與一般理解在概念上能達成一致,應該不會有任何疑問的。可如果用 select rownum,c1 from t1 where rownum > 10 (如果寫下這樣的查詢語句,這時候在您的頭腦中應該是想得到表中后面10條記錄),你就會發現,顯示出來的結果要讓您失望了,也許您還會懷疑是不誰刪了一 些記錄,然后查看記錄數,仍然是 20 條啊?那問題是出在哪呢?先好好理解 rownum 的意義吧。因為ROWNUM是對結果集加的一個偽列,即先查到結果集之后再加上去的一個列 (強調:先要有結果集)。簡單的說 rownum 是對符合條件結果的序列號。它總是從1開始排起的。所以你選出的結果不可能沒有1,而有其他大于1的值。所以您沒辦法期望得到下面的結果集:11 aaaaaaaa 12 bbbbbbb 13 ccccccc .................rownum >10 沒有記錄,因為第一條不滿足去掉的話,第二條的ROWNUM又成了1,所以永遠沒有滿足條件的記錄。或者可以這樣理解:ROWNUM是一個序列,是oracle數據庫從數據文件或緩沖區中讀取數據的順序。它取得第一條記錄則rownum值為1,第二條為2,依次類 推。如果你用>,>=,=,between...and這些條件,因為從緩沖區或數據文件中得到的第一條記錄的rownum為1,則被刪除, 接著取下條,可是它的rownum還是1,又被刪除,依次類推,便沒有了數據。有了以上從不同方面建立起來的對 rownum 的概念,那我們可以來認識使用 rownum 的幾種現像1. select rownum,c1 from t1 where rownum != 10 為何是返回前9條數據呢?它與 select rownum,c1 from tablename where rownum < 10 返回的結果集是一樣的呢? 因為是在查詢到結果集后,顯示完第 9 條記錄后,之后的記錄也都是 != 10,或者 >=10,所以只顯示前面9條記錄。也可以這樣理解,rownum 為9后的記錄的 rownum為10,因條件為 !=10,所以去掉,其后記錄補上,rownum又是10,也去掉,如果下去也就只會顯示前面9條記錄了2. 為什么 rownum >1 時查不到一條記錄,而 rownum >0 或 rownum >=1 卻總顯示所以的記錄 因為 rownum 是在查詢到的結果集后加上去的,它總是從1開始3. 為什么 between 1 and 10 或者 between 0 and 10 能查到結果,而用 between 2 and 10 卻得不到結果 原因同上一樣,因為 rownum 總是從 1 開始從上可以看出,任何時候想把 rownum = 1 這條記錄拋棄是不對的,它在結果集中是不可或缺的,少了rownum=1 就像空中樓閣一般不能存在,所以你的 rownum 條件要包含到 1但如果就是想要用 rownum > 10 這種條件的話話就要用嵌套語句,把 rownum 先生成,然后對他進行查詢。 select * from (selet rownum as rn,t1.* from a where ...) where rn >10一般代碼中對結果集進行分頁就是這么干的。另外:rowid 與 rownum 雖都被稱為偽列,但它們的存在方式是不一樣的,rowid 可以說是物理存在的,表示記錄在表空間中的唯一位置ID,在DB中唯一。只要記錄沒被搬動過,rowid是不變的。rowid 相對于表來說又像表中的一般列,所以以 rowid 為條件就不會有 rownum那些情況發生。 另外還要注意:rownum不能以任何基表的名稱作為前綴。 ---------------------------------------------------------------------Oracle中rownum的基本用法 對于rownum來說它是oracle系統順序分配為從查詢返回的行的編號,返回的第一行分配的是1,第二行是2,依此類推,這個偽字段可以用于限制查詢返回的總行數,且rownum不能以任何表的名稱作為前綴。(1) rownum 對于等于某值的查詢條件 如果希望找到學生表中第一條學生的信息,可以使用rownum=1作為條件。但是想找到學生表中第二條學生的信息,使用rownum=2結果查不到數據。因為rownum都是從1開始,但是1以上的自然數在rownum做等于判斷是時認為都是false條件,所以無法查到rownum = n(n>1的自然數)。 SQL> select rownum,id,name from student where rownum=1;(可以用在限制返回記錄條數的地方,保證不出錯,如:隱式游標) SQL> select rownum,id,name from student where rownum =2; ROWNUM ID NAME ---------- ------ ---------------------------------------------------(2)rownum對于大于某值的查詢條件如果想找到從第二行記錄以后的記錄,當使用rownum>2是查不出記錄的,原因是由于rownum是一個總是從1開始的偽列,Oracle 認為rownum> n(n>1的自然數)這種條件依舊不成立,所以查不到記錄。查找到第二行以后的記錄可使用以下的子查詢方法來解決。注意子查詢中的rownum必須要有別名,否則還是不會查出記錄來,這是因為rownum不是某個表的列,如果不起別名的話,無法知道rownum是子查詢的列還是主查詢的列。 SQL>select * from(select rownum no ,id,name from student) where no>2;NO ID NAME ---------- ------ ---------------------------------------------------3 200003 李三4 200004 趙四(3)rownum對于小于某值的查詢條件 rownum對于rownum<n((n>1的自然數)的條件認為是成立的,所以可以找到記錄。 SQL> select rownum,id,name from student where rownum <3;ROWNUM ID NAME ---------- ------ ---------------------------------------------------1 200001 張一2 200002 王二查詢rownum在某區間的數據,必須使用子查詢。例如要查詢rownum在第二行到第三行之間的數據,包括第二行和第三行數據,那么我們只能寫以下語句,先讓它返回小于等于三的記錄行,然后在主查詢中判斷新的rownum的別名列大于等于二的記錄行。但是這樣的操作會在大數據集中影響速度。 SQL> select * from (select rownum no,id,name from student where rownum<=3 ) where no >=2;NO ID NAME ---------- ------ ---------------------------------------------------2 200002 王二3 200003 李三(4)rownum和排序 Oracle中的rownum的是在取數據的時候產生的序號,所以想對指定排序的數據去指定的rowmun行數據就必須注意了。 SQL> select rownum ,id,name from student order by name;ROWNUM ID NAME ---------- ------ ---------------------------------------------------3 200003 李三2 200002 王二1 200001 張一4 200004 趙四 可以看出,rownum并不是按照name列來生成的序號。系統是按照記錄插入時的順序給記錄排的號,rowid也是順序分配的。為了解決這個問題,必須使用子查詢; SQL> select rownum ,id,name from (select * from student order by name);ROWNUM ID NAME ---------- ------ ---------------------------------------------------1 200003 李三2 200002 王二3 200001 張一4 200004 趙四 這樣就成了按name排序,并且用rownum標出正確序號(有小到大) 筆者在工作中有一上百萬條記錄的表,在jsp頁面中需對該表進行分頁顯示,便考慮用rownum來作,下面是具體方法(每頁顯示20條): “select * from tabname where rownum<20 order by name" 但卻發現oracle卻不能按自己的意愿來執行,而是先隨便取20條記錄,然后再order by,后經咨詢oracle,說rownum確實就這樣,想用的話,只能用子查詢來實現先排序,后rownum,方法如下: "select * from (select * from tabname order by name) where rownum<20",但這樣一來,效率會低很多。 后經筆者試驗,只需在order by 的字段上加主鍵或索引即可讓oracle先按該字段排序,然后再rownum;方法不變: “select * from tabname where rownum<20 order by name"取得某列中第N大的行select column_name from (select table_name.*,dense_rank() over (order by column desc) rank from table_name) where rank = &N; 假如要返回前5條記錄:select * from tablename where rownum<6;(或是rownum <= 5 或是rownum != 6) 假如要返回第5-9條記錄:select * from tablename where … and rownum<10 minus select * from tablename where … and rownum<5 order by name 選出結果后用name排序顯示結果。(先選再排序)注意:只能用以上符號(<、<=、!=)。select * from tablename where rownum != 10;返回的是前9條記錄。 不能用:>,>=,=,Between...and。由于rownum是一個總是從1開始的偽列,Oracle 認為這種條件不成立。另外,這個方法更快:select * from ( select rownum r,a from yourtable where rownum <= 20 order by name ) where r > 10 這樣取出第11-20條記錄!(先選再排序再選)要先排序再選則須用select嵌套:內層排序外層選。 rownum是隨著結果集生成的,一旦生成,就不會變化了;同時,生成的結果是依次遞加的,沒有1就永遠不會有2! rownum 是在查詢集合產生的過程中產生的偽列,并且如果where條件中存在 rownum 條件的話,則:1: 假如判定條件是常量,則: 只能 rownum = 1, <= 大于1 的自然數, = 大于1 的數是沒有結果的;大于一個數也是沒有結果的 即 當出現一個 rownum 不滿足條件的時候則 查詢結束 this is stop key(一個不滿足,系統將該記錄過濾掉,則下一條記錄的rownum還是這個,所以后面的就不再有滿足記錄,this is stop key);2: 假如判定值不是常量,則:若條件是 = var , 則只有當 var 為1 的時候才滿足條件,這個時候不存在 stop key ,必須進行full scan ,對每個滿足其他where條件的數據進行判定,選出一行后才能去選rownum=2的行……分頁查詢語句: 1:單表查詢SELECT * FROM (SELECT t.*,ROWNUM r FROM TABLE t WHERE ROWNUM <= pageNumber*pageSize) WHERE r >(pageNumber)*pageSize2:兩張表聯查SELECT * FROM (SELECT ROWNUM RN,XX.* FROM (SELECT 表名.字段名, 表名.字段名, 表名.字段名... FROM TABLE1 t1, TABLE2 t2 WHERE t1.字段=t2.字段) XX WHERE ROWNUM<=pageSize*pageNumber) WHERE RN >(pageNumber-1)*pageSize

select into與insert into區別

我們經常會遇到需要表復制的情況,如將一個table1的數據的部分字段復制到table2中,或者將整個table1復制到table2中,這時候我們就要使用SELECT INTO 和 INSERT INTO SELECT 表復制語句了。1.INSERT INTO SELECT語句語句形式為:Insert into Table2(field1,field2,...) select value1,value2,... from Table1注意:(1)要求目標表Table2必須存在,并且字段field,field2...也必須存在(2)注意Table2的主鍵約束,如果Table2有主鍵而且不為空,則 field1, field2...中必須包括主鍵(3)注意語法,不要加values,和插入一條數據的sql混了,不要寫成:Insert into Table2(field1,field2,...) values (select value1,value2,... from Table1)由于目標表Table2已經存在,所以我們除了插入源表Table1的字段外,還可以插入常量。示例如下:+ expand sourceview plaincopy to clipboardprint--1.創建測試表 create TABLE Table1 ( a varchar(10), b varchar(10), c varchar(10))create TABLE Table2 ( a varchar(10), c varchar(10), d int)--2.創建測試數據 Insert into Table1 values('趙','asds','90') Insert into Table1 values('錢','asds','100') Insert into Table1 values('孫','asds','80') Insert into Table1 values('李','asds',null) select * from Table2--3.INSERT INTO SELECT語句復制表數據 Insert into Table2(a, c, d) select a,c,5 from Table1--4.顯示更新后的結果 select * from Table2 --5.刪除測試表 drop TABLE Table1 drop TABLE Table22.SELECT INTO FROM語句語句形式為:SELECT vale1, value2 into Table2 from Table1要求目標表Table2不存在,因為在插入時會自動創建表Table2,并將Table1中指定字段數據復制到Table2中。示例如下:view plaincopy to clipboardprint?--1.創建測試表 create TABLE Table1 ( a varchar(10), b varchar(10), c varchar(10))--2.創建測試數據 Insert into Table1 values('趙','asds','90') Insert into Table1 values('錢','asds','100') Insert into Table1 values('孫','asds','80') Insert into Table1 values('李','asds',null) --3.SELECT INTO FROM語句創建表Table2并復制數據 select a,c INTO Table2 from Table1 --4.顯示更新后的結果 select * from Table2 --5.刪除測試表 drop TABLE Table1 drop TABLE Table2 注意:如果在sql/plus或者PL/SQL執行這條語句,會報"ORA-00905:缺失關鍵字"錯誤,原因是PL/Sql與T-SQL的區別。 T-SQL中該句正常,但PL/SQL中解釋是: select..into is part of PL/SQL language which means you have to use it inside a PL/SQL block. You can not use it in a SQL statement outside of PL/SQL. 即不能單獨作為一條sql語句執行,一般在PL/SQL程序塊(block)中使用。如果想在PL/SQL中實現該功能,可使用Create table newTable as select * from ...: 如: create table NewTable as select * from ATable;NewTable 除了沒有鍵,其他的和ATable一樣---------SQL SELECT INTO語法介紹 SQL SELECT INTO 語句可用于創建表的備份復件。 SELECT INTO 語句 SELECT INTO 語句從一個表中選取數據,然后把數據插入另一個表中。 SELECT INTO 語句常用于創建表的備份復件或者用于對記錄進行存檔。 SQL SELECT INTO 語法 您可以把所有的列插入新表: SELECT * INTO new_table_name [IN externaldatabase] FROM old_tablename 或者只把希望的列插入新表: SELECT column_name(s) INTO new_table_name [IN externaldatabase] FROM old_tablename SQL SELECT INTO 實例 - 制作備份復件 下面的例子會制作 "Persons" 表的備份復件: SELECT * INTO Persons_backup FROM Persons IN 子句可用于向另一個數據庫中拷貝表: SELECT * INTO Persons IN 'Backup.mdb' FROM Persons 如果我們希望拷貝某些域,可以在 SELECT 語句后列出這些域: SELECT LastName,FirstName INTO Persons_backup FROM Persons SQL SELECT INTO 實例 - 帶有 WHERE 子句 我們也可以添加 WHERE 子句。 下面的例子通過從 "Persons" 表中提取居住在 "Beijing" 的人的信息,創建了一個帶有兩個列的名為 "Persons_backup" 的表: SELECT LastName,Firstname INTO Persons_backup FROM Persons WHERE City='Beijing' SQL SELECT INTO 實例 - 被連接的表 從一個以上的表中選取數據也是可以做到的。 下面的例子會創建一個名為 "Persons_Order_Backup" 的新表,其中包含了從 Persons 和 Orders 兩個表中取得的信息: SELECT Persons.LastName,Orders.OrderNo INTO Persons_Order_Backup FROM Persons INNER JOIN Orders ON Persons.Id_P=Orders.Id_P ------------------------------------------- select into from 和 insert into select都是用來復制表,兩者的主要區別為: select into from 要求目標表不存在,因為在插入時會自動創建。insert into select from 要求目標表存在。下面分別介紹兩者語法一、INSERT INTO SELECT語句(見下)1、語句形式insert into Table1(field1,field2...) select value1,value2... from Table22、注意(1)要求Table1存在,且字段field1,field2...也存在(2)注意Table2的主鍵約束,若Table2中有主鍵且不為空,則字段field1,field2...中也必須包含主鍵(3)注意語法,不要加values,和插入一條數據的sql混了,不要寫成:Insert into Table2(field1,field2,...) values (select value1,value2,... from Table1) (4)由于目標表Table2已經存在,所以我們除了插入源表Table1的字段外,還可以插入常量。3、完整實例: SQL 代碼 復制--1.創建測試表create TABLE Table1(a varchar(10),b varchar(10),c varchar(10), CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED(a ASC)) ON [PRIMARY] create TABLE Table2(a varchar(10),c varchar(10),d int, CONSTRAINT [PK_Table2] PRIMARY KEY CLUSTERED(a ASC)) ON [PRIMARY] GO --2.創建測試數據Insert into Table1 values('趙','asds','90') Insert into Table1 values('錢','asds','100') Insert into Table1 values('孫','asds','80') Insert into Table1 values('李','asds',null) GO select * from Table2 --3.INSERT INTO SELECT語句復制表數據Insert into Table2(a, c, d) select a,c,5 from Table1 GO --4.顯示更新后的結果select * from Table2 GO --5.刪除測試表drop TABLE Table1 drop TABLE Table2二、SELECT INTO FROM語句 語句形式為:SELECT vale1, value2 into Table2 from Table1 要求目標表Table2不存在,因為在插入時會自動創建表Table2,并將Table1中指定字段數據復制到Table2中 。完整實例: SQL 代碼 復制--1.創建測試表create TABLE Table1(a varchar(10),b varchar(10),c varchar(10), CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED(a ASC)) ON [PRIMARY] GO --2.創建測試數據Insert into Table1 values('趙','asds','90') Insert into Table1 values('錢','asds','100') Insert into Table1 values('孫','asds','80') Insert into Table1 values('李','asds',null) GO --3.SELECT INTO FROM語句創建表Table2并復制數據select a,c INTO Table2 from Table1 GO --4.顯示更新后的結果select * from Table2 GO --5.刪除測試表drop TABLE Table1 drop TABLE Table2不幸的是,我用select into沒有成功,因為時間問題,還是用了復制表的基本語句(從oldTable復制到newTable): 1、create table newTable as select * from oldTabel; 既復制表結構,也復制表內容; 2、create table newTable as select * from oldTable where 1=2; 只復制表結構,不復制表內容; 3、insert into newTable select * form oldTable; 不復制表結構,只復制表內容 或者 select value1,value2 into newTable form oldTable;

?merge into的使用

我們操作數據庫的時候,有時候會遇到insertOrUpdate這種需求。如果數據庫中存在數據就update,如果不存在就insert。以前的時候,需要額外select查詢一下,如果有數據就update,如果沒有數據就insert。而現在Orcale數據庫都提供了 MERGE 方法來處理這種需求。MERGE 命令使用一條語句從一個或者多個數據源中完成對表的更新和插入數據。MERGE 語法:MERGE INTO [your table-name] [rename your table here] USING ( [write your query here] )[rename your query-sql and using just like a table] ON ([conditional expression here] AND [...]...) WHEN MATHED THEN [here you can execute some update sql or something else ] WHEN NOT MATHED THEN [execute something else here ! ] 使用例子:create table TEST (ID INTEGER,VALUE VARCHAR2(255) ); insert into TEST values (1, 'test1'); insert into TEST values (2, 'test2'); 我們想插入一條數據 {ID=2,NAME='newtest2'} 那么可以這么寫MERGE INTO TEST T1 USING (SELECT '2' as A FROM dual) T2 on (T1.ID=T2.ID) WHEN MATCHED THEN UPDATE SET T1.NAME='newtest2' WHEN NOT MATCHED THEN INSERT (T1.ID, T1.NAME) VALUES ('1', 'newtest2'); 如果ID為2的數據存在那么 UPDATE,如果不存在INSERT注意事項:Merge Into的原理是,從using 搜出來的結果逐條與on條件匹配,然后決定是update還是Insert。 當USING后面的sql沒有查詢到數據的時候,Merge Into語句是不會執行update和Insert操作的。所以要想讓Merge Into正常運行,要保證USING 后面的SELECT有數據,個人喜歡使用DUAL表作為USING后的表,方便自己控制。 ------------------------------------------oracle merge into 用法詳解 1. MERGE INTO 的用途MERGE INTO 是Oracle 9i以后才出現的新的功能。那這個功能 是什么呢?簡單來說,就是:“有則更新,無則插入”從這句話里,應該可以理解到,merge into 操作一個對象'A'的時候,要有另外一個結果集做為源數據 'B'.‘merge into’ 將B中的數據與A中的數據按照一定條件'C'進行對比,如果 A中數據滿足C條件,則進行update操作,如果不滿足條件 'C',則進行insert操作。(請注意這種對應關系)2、 語法結構MERGE [INTO] [schema.]table [alias]USING {[schema.]table|views|query} [alias]ON {condition}WHEN MATCHED THEN UPDATE SET {clause}WHEN NOT MATCHED THEN INSERT VALUES {clause}我們可以用于單條數據的處理,也可以用于數據的批處理。對于merge into來說,那都是張飛吃豆芽兒,小菜一碟兒。而且效率要比單獨執行update+insert 操作效率要高。但是請注意,using語句中的結果集 B不可以與merge into 的對象A相同,否則,會因為結果集A,B恒等。當 on() 進行等值判斷時,只可以進行update操作,不能進行insert 操作,當 on() 進行不等值判斷時,只可以進行insert操作,不能進行update操作。首先創建示例表: create table PRODUCTS(PRODUCT_ID INTEGER,PRODUCT_NAME VARCHAR2(60),CATEGORY VARCHAR2(60));insert into PRODUCTS values (1501, 'VIVITAR 35MM', 'ELECTRNCS');insert into PRODUCTS values (1502, 'OLYMPUS IS50', 'ELECTRNCS');insert into PRODUCTS values (1600, 'PLAY GYM', 'TOYS');insert into PRODUCTS values (1601, 'LAMAZE', 'TOYS');insert into PRODUCTS values (1666, 'HARRY POTTER', 'DVD');commit;create table NEWPRODUCTS(PRODUCT_ID INTEGER,PRODUCT_NAME VARCHAR2(60),CATEGORY VARCHAR2(60));insert into NEWPRODUCTS values (1502, 'OLYMPUS CAMERA', 'ELECTRNCS');insert into NEWPRODUCTS values (1601, 'LAMAZE', 'TOYS');insert into NEWPRODUCTS values (1666, 'HARRY POTTER', 'TOYS');insert into NEWPRODUCTS values (1700, 'WAIT INTERFACE', 'BOOKS');commit;1.可省略的UPDATE或INSERT子句update使用,省略insert:MERGE INTO products pUSING newproducts npON (p.product_id = np.product_id)WHEN MATCHED THENUPDATESET p.product_name = np.product_name,p.category = np.category;insert使用,省略update:MERGE INTO products pUSING newproducts npON (p.product_id = np.product_id)WHEN NOT MATCHED THENINSERTVALUES (np.product_id, np.product_name,np.category);2、帶條件的Updates和Inserts子句你能夠添加WHERE子句到UPDATE或INSERT子句中去, 來跳過update或insert操作對某些行的處理.下面例子根據表NEWPRODUCTS來更新表PRODUCTS數據,根據條件category進行更新:MERGE INTO products pUSING newproducts npON (p.product_id = np.product_id)WHEN MATCHED THENUPDATESET p.product_name = np.product_nameWHERE p.category = np.category;MERGE INTO products pUSING newproducts npON (p.product_id = np.product_id)WHEN MATCHED THENUPDATESET p.product_name = np.product_name,p.category = np.category WHERE p.category = 'DVD' WHEN NOT MATCHED THEN INSERT VALUES (np.product_id, np.product_name, np.category) WHERE np.category != 'BOOKS'3.兩表連接無條件的Inserts 你能夠不用連接源表和目標表就把源表的數據插入到目標表中. 這對于你想插入所有行到目標表時是非常有用的. Oracle 10g現在支持在ON條件中使用常量過濾謂詞. 舉個常量過濾謂詞例子ON (1=0). 下面例子從源表插入行到表PRODUCTS, 不檢查這些行是否在表PRODUCTS中存在:SQL> MERGE INTO products pUSING newproducts npON (1=0)WHEN NOT MATCHED THENINSERTVALUES (np.product_id, np.product_name, np.category)WHERE np.category = 'BOOKS' -----------------------------------------------------------

樹操作遞歸查詢

---- 向上遞歸 id 最小級別 select distinct text,typefrom t_test_plan_ganttmodels t start with id = '0d143095102e4e39b52defbaf1e3c993' connect by prior parent = id ---- 向下遞歸 id 最大級別 select distinct text ,typefrom t_test_plan_ganttmodels t start with t.id = '0d143095102e4e39b52defbaf1e3c993' connect by prior id = parent --------------------------------------------------------------- Oracle 樹操作、遞歸查詢(select…start with…connect by…prior) 一、Oracle中start with…connect by prior子句用法connect by 是結構化查詢中用到的,其基本語法是: select … from tablename start with 條件1 connect by 條件2 where 條件3; 例: select * from table start with org_id = ‘HBHqfWGWPy’ connect by prior org_id = parent_id;簡單說來是將一個樹狀結構存儲在一張表里,比如一個表中存在兩個字段: org_id,parent_id那么通過表示每一條記錄的parent是誰,就可以形成一個樹狀結構。用上述語法的查詢可以取得這棵樹的所有記錄。其中:條件1 是根結點的限定語句,當然可以放寬限定條件,以取得多個根結點,實際就是多棵樹。條件2 是連接條件,其中用PRIOR表示上一條記錄,比如 CONNECT BY PRIOR org_id = parent_id;就是說上一條記錄的org_id 是本條記錄的parent_id,即本記錄的父親是上一條記錄。條件3 是過濾條件,用于對返回的所有記錄進行過濾。簡單介紹如下:在掃描樹結構表時,需要依此訪問樹結構的每個節點,一個節點只能訪問一次,其訪問的步驟如下:第一步:從根節點開始;第二步:訪問該節點;第三步:判斷該節點有無未被訪問的子節點,若有,則轉向它最左側的未被訪問的子節,并執行第二步,否則執行第四步;第四步:若該節點為根節點,則訪問完畢,否則執行第五步;第五步:返回到該節點的父節點,并執行第三步驟。總之:掃描整個樹結構的過程也即是中序遍歷樹的過程。1.樹結構的描述樹結構的數據存放在表中,數據之間的層次關系即父子關系,通過表中的列與列間的關系來描述,如EMP表中的EMPNO和MGR。EMPNO表示該雇員的編號,MGR表示領導該雇員的人的編號,即子節點的MGR值等于父節點的EMPNO值。在表的每一行中都有一個表示父節點的MGR(除根節點外),通過每個節點的父節點,就可以確定整個樹結構。在SELECT命令中使用CONNECT BY 和START WITH 子句可以查詢表中的樹型結構關系。其命令格式如下: SELECT . . . CONNECT BY {PRIOR 列名1=列名2|列名1=PRIOR 裂名2} [START WITH];其中:CONNECT BY子句說明每行數據將是按層次順序檢索,并規定將表中的數據連入樹型結構的關系中。PRIOR運算符必須放置在連接關系的兩列中某一個的前面。對于節點間的父子關系,PRIOR運算符在一側表示父節點,在另一側表示子節點,從而確定查找樹結構是的順序是自頂向下還是自底向上。在連接關系中,除了可以使用列名外,還允許使用列表達式。START WITH 子句為可選項,用來標識哪個節點作為查找樹型結構的根節點。若該子句被省略,則表示所有滿足查詢條件的行作為根節點。START WITH:不但可以指定一個根節點,還可以指定多個根節點。2.關于PRIOR運算符PRIOR被放置于等號前后的位置,決定著查詢時的檢索順序。PRIOR被置于CONNECT BY子句中等號的前面時,則強制從根節點到葉節點的順序檢索,即由父節點向子節點方向通過樹結構,我們稱之為自頂向下的方式。如:CONNECT BY PRIOR EMPNO=MGRPIROR運算符被置于CONNECT BY 子句中等號的后面時,則強制從葉節點到根節點的順序檢索,即由子節點向父節點方向通過樹結構,我們稱之為自底向上的方式。例如:CONNECT BY EMPNO=PRIOR MGR在這種方式中也應指定一個開始的節點。3.定義查找起始節點在自頂向下查詢樹結構時,不但可以從根節點開始,還可以定義任何節點為起始節點,以此開始向下查找。這樣查找的結果就是以該節點為開始的結構樹的一枝。4.使用LEVEL在具有樹結構的表中,每一行數據都是樹結構中的一個節點,由于節點所處的層次位置不同,所以每行記錄都可以有一個層號。層號根據節點與根節點的距離確定。不論從哪個節點開始,該起始根節點的層號始終為1,根節點的子節點為2, 依此類推。圖1.2就表示了樹結構的層次。5.節點和分支的裁剪在對樹結構進行查詢時,可以去掉表中的某些行,也可以剪掉樹中的一個分支,使用WHERE子句來限定樹型結構中的單個節點,以去掉樹中的單個節點,但它卻不影響其后代節點(自頂向下檢索時)或前輩節點(自底向頂檢索時)。6.排序顯示象在其它查詢中一樣,在樹結構查詢中也可以使用ORDER BY 子句,改變查詢結果的顯示順序,而不必按照遍歷樹結構的順序。二、例子1、準備測試表和測試數據1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 --菜單目錄結構表 create table tb_menu( &nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp; number(10) not null, --主鍵id &nbsp;&nbsp; title&nbsp; varchar2(50), --標題 &nbsp;&nbsp; parent number(10) --parent id )--父菜單 insert into tb_menu(id, title, parent) values(1, '父菜單1',null); insert into tb_menu(id, title, parent) values(2, '父菜單2',null); insert into tb_menu(id, title, parent) values(3, '父菜單3',null); insert into tb_menu(id, title, parent) values(4, '父菜單4',null); insert into tb_menu(id, title, parent) values(5, '父菜單5',null); --一級菜單 insert into tb_menu(id, title, parent) values(6, '一級菜單6',1); insert into tb_menu(id, title, parent) values(7, '一級菜單7',1); insert into tb_menu(id, title, parent) values(8, '一級菜單8',1); insert into tb_menu(id, title, parent) values(9, '一級菜單9',2); insert into tb_menu(id, title, parent) values(10, '一級菜單10',2); insert into tb_menu(id, title, parent) values(11, '一級菜單11',2); insert into tb_menu(id, title, parent) values(12, '一級菜單12',3); insert into tb_menu(id, title, parent) values(13, '一級菜單13',3); insert into tb_menu(id, title, parent) values(14, '一級菜單14',3); insert into tb_menu(id, title, parent) values(15, '一級菜單15',4); insert into tb_menu(id, title, parent) values(16, '一級菜單16',4); insert into tb_menu(id, title, parent) values(17, '一級菜單17',4); insert into tb_menu(id, title, parent) values(18, '一級菜單18',5); insert into tb_menu(id, title, parent) values(19, '一級菜單19',5); insert into tb_menu(id, title, parent) values(20, '一級菜單20',5); --二級菜單 insert into tb_menu(id, title, parent) values(21, '二級菜單21',6); insert into tb_menu(id, title, parent) values(22, '二級菜單22',6); insert into tb_menu(id, title, parent) values(23, '二級菜單23',7); insert into tb_menu(id, title, parent) values(24, '二級菜單24',7); insert into tb_menu(id, title, parent) values(25, '二級菜單25',8); insert into tb_menu(id, title, parent) values(26, '二級菜單26',9); insert into tb_menu(id, title, parent) values(27, '二級菜單27',10); insert into tb_menu(id, title, parent) values(28, '二級菜單28',11); insert into tb_menu(id, title, parent) values(29, '二級菜單29',12); insert into tb_menu(id, title, parent) values(30, '二級菜單30',13); insert into tb_menu(id, title, parent) values(31, '二級菜單31',14); insert into tb_menu(id, title, parent) values(32, '二級菜單32',15); insert into tb_menu(id, title, parent) values(33, '二級菜單33',16); insert into tb_menu(id, title, parent) values(34, '二級菜單34',17); insert into tb_menu(id, title, parent) values(35, '二級菜單35',18); insert into tb_menu(id, title, parent) values(36, '二級菜單36',19); insert into tb_menu(id, title, parent) values(37, '二級菜單37',20); --三級菜單 insert into tb_menu(id, title, parent) values(38, '三級菜單38',21); insert into tb_menu(id, title, parent) values(39, '三級菜單39',22); insert into tb_menu(id, title, parent) values(40, '三級菜單40',23); insert into tb_menu(id, title, parent) values(41, '三級菜單41',24); insert into tb_menu(id, title, parent) values(42, '三級菜單42',25); insert into tb_menu(id, title, parent) values(43, '三級菜單43',26); insert into tb_menu(id, title, parent) values(44, '三級菜單44',27); insert into tb_menu(id, title, parent) values(45, '三級菜單45',28); insert into tb_menu(id, title, parent) values(46, '三級菜單46',28); insert into tb_menu(id, title, parent) values(47, '三級菜單47',29); insert into tb_menu(id, title, parent) values(48, '三級菜單48',30); insert into tb_menu(id, title, parent) values(49, '三級菜單49',31); insert into tb_menu(id, title, parent) values(50, '三級菜單50',31); commit;select * from tb_menu; parent字段存儲的是上級id,如果是頂級父節點,該parent為null(得補充一句,當初的確是這樣設計的,不過現在知道,表中最好別有null記錄,這會引起全文掃描,建議改成0代替)。2、樹操作 我們從最基本的操作,逐步列出樹查詢中常見的操作,所有查詢出來的節點以家族中的輩份作比方。1)、查找樹中的所有頂級父節點(輩份最長的人)。 假設這個樹是個目錄結構,那么第一個操作總是找出所有的頂級節點,再根據該節點找到其下屬節點。1 select * from tb_menu m where m.parent is null; 2)、查找一個節點的直屬子節點(所有兒子)。 如果查找的是直屬子類節點,也是不用用到樹型查詢的。1 select * from tb_menu m where m.parent=1; 3)、查找一個節點的所有直屬子節點(所有后代)。1 select * from tb_menu m start with m.id=1 connect by m.parent=prior m.id; 這個查找的是id為1的節點下的所有直屬子類節點,包括子輩的和孫子輩的所有直屬節點。4)、查找一個節點的直屬父節點(父親)。 如果查找的是節點的直屬父節點,也是不用用到樹型查詢的。1 2 3 4 --c-->child, p->parent select c.id, c.title, p.id parent_id, p.title parent_title from tb_menu c, tb_menu p where c.parent=p.id and c.id=6 5)、查找一個節點的所有直屬父節點(祖宗)。1 select * from tb_menu m start with m.id=38 connect by prior m.parent=m.id; 這里查找的就是id為1的所有直屬父節點,打個比方就是找到一個人的父親、祖父等。但是值得注意的是這個查詢出來的結果的順序是先列出子類節點再列出父類節點,姑且認為是個倒序吧。上面列出兩個樹型查詢方式,第3條語句和第5條語句,這兩條語句之間的區別在于prior關鍵字的位置不同,所以決定了查詢的方式不同。 當parent = prior id時,數據庫會根據當前的id迭代出parent與該id相同的記錄,所以查詢的結果是迭代出了所有的子類記錄;而prior parent = id時,數據庫會跟據當前的parent來迭代出與當前的parent相同的id的記錄,所以查詢出來的結果就是所有的父類結果。以下是一系列針對樹結構的更深層次的查詢,這里的查詢不一定是最優的查詢方式,或許只是其中的一種實現而已。6)、查詢一個節點的兄弟節點(親兄弟)。1 2 3 --m.parent=m2.parent-->同一個父親 select * from tb_menu m where exists (select * from tb_menu m2 where m.parent=m2.parent and m2.id=6) 7)、查詢與一個節點同級的節點(族兄弟)。 如果在表中設置了級別的字段,那么在做這類查詢時會很輕松,同一級別的就是與那個節點同級的,在這里列出不使用該字段時的實現!1 2 3 4 5 6 7 8 with tmp as(select a.*, level leaf from tb_menu a start with a.parent is null connect by a.parent = prior a.id) select * from tmp where leaf = (select leaf from tmp where id = 50); 這里使用兩個技巧,一個是使用了level來標識每個節點在表中的級別,還有就是使用with語法模擬出了一張帶有級別的臨時表。8)、查詢一個節點的父節點的的兄弟節點(伯父與叔父)。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 with tmp as(select tb_menu.*, level levfrom tb_menustart with parent is nullconnect by parent = prior id)select b.* from tmp b,(select *from tmpwhere id = 21 and lev = 2) a where b.lev = 1union allselect * from tmp where parent = (select distinct x.idfrom tmp x, --祖父tmp y, --父親(select *from tmpwhere id = 21 and lev > 2) z --兒子where y.id = z.parent and x.id = y.parent); 這里查詢分成以下幾步。 首先,將第7個一樣,將全表都使用臨時表加上級別; 其次,根據級別來判斷有幾種類型,以上文中舉的例子來說,有三種情況: (1)當前節點為頂級節點,即查詢出來的lev值為1,那么它沒有上級節點,不予考慮。 (2)當前節點為2級節點,查詢出來的lev值為2,那么就只要保證lev級別為1的就是其上級節點的兄弟節點。 (3)其它情況就是3以及以上級別,那么就要選查詢出來其上級的上級節點(祖父),再來判斷祖父的下級節點都是屬于該節點的上級節點的兄弟節點。 最后,就是使用union將查詢出來的結果進行結合起來,形成結果集。9)、查詢一個節點的父節點的同級節點(族叔)。 這個其實跟第7種情況是相同的。1 2 3 4 5 6 7 8 with tmp as(select a.*, level leaf from tb_menu a start with a.parent is null connect by a.parent = prior a.id) select * from tmp where leaf = (select leaf from tmp where id = 6) - 1; 基本上,常見的查詢在里面了,不常見的也有部分了。其中,查詢的內容都是節點的基本信息,都是數據表中的基本字段,但是在樹查詢中還有些特殊需求,是對查詢數據進行了處理的,常見的包括列出樹路徑等。補充一個概念,對于數據庫來說,根節點并不一定是在數據庫中設計的頂級節點,對于數據庫來說,根節點就是start with開始的地方。下面列出的是一些與樹相關的特殊需求。10)、名稱要列出名稱全部路徑。 這里常見的有兩種情況,一種是從頂級列出,直到當前節點的名稱(或者其它屬性);一種是從當前節點列出,直到頂級節點的名稱(或其它屬性)。舉地址為例:國內的習慣是從省開始、到市、到縣、到居委會的,而國外的習慣正好相反(老師說的,還沒接過國外的郵件,誰能寄個瞅瞅 )。 從頂部開始:1 2 3 4 5 select sys_connect_by_path (title, '/') from tb_menu where id = 50 start with parent is null connect by parent = prior id; 從當前節點開始:1 2 3 4 select sys_connect_by_path (title, '/') from tb_menu start with id = 50 connect by prior parent = id; 在這里我又不得不放個牢騷了。oracle只提供了一個sys_connect_by_path函數,卻忘了字符串的連接的順序。在上面的例子中,第一個sql是從根節點開始遍歷,而第二個sql是直接找到當前節點,從效率上來說已經是千差萬別,更關鍵的是第一個sql只能選擇一個節點,而第二個sql卻是遍歷出了一顆樹來。再次ps一下。sys_connect_by_path函數就是從start with開始的地方開始遍歷,并記下其遍歷到的節點,start with開始的地方被視為根節點,將遍歷到的路徑根據函數中的分隔符,組成一個新的字符串,這個功能還是很強大的。11)、列出當前節點的根節點。 在前面說過,根節點就是start with開始的地方。1 2 3 4 select connect_by_root title, tb_menu.* from tb_menu start with id = 50 connect by prior parent = id; connect_by_root函數用來列的前面,記錄的是當前節點的根節點的內容。12)、列出當前節點是否為葉子。 這個比較常見,尤其在動態目錄中,在查出的內容是否還有下級節點時,這個函數是很適用的。1 2 3 4 select connect_by_isleaf, tb_menu.* from tb_menu start with parent is null connect by parent = prior id; connect_by_isleaf函數用來判斷當前節點是否包含下級節點,如果包含的話,說明不是葉子節點,這里返回0;反之,如果不包含下級節點,這里返回1。至此,oracle樹型查詢基本上講完了,以上的例子中的數據是使用到做過的項目中的數據,因為里面的內容可能不好理解,所以就全部用一些新的例子來進行闡述。以上所有sql都在本機上測試通過,也都能實現相應的功能,但是并不能保證是解決這類問題的最優方案(如第8條明顯寫成存儲過程會更好). ----------------------------------------------------------------oracle遞歸查詢也有叫樹查詢的,語法結構為: select? * from ?tb? start with id=1 connect by prior id=id2 (prior也可以放在等號后面:id=prior id2 ,這樣查詢的結果等同于交換變量的位置:prior id2=id).之所以叫遞歸查詢是因為后一次的查詢條件是依賴前一次的記錄進行的,如prior id2=id? 意思就是當前要查詢的id等于前一條記錄的id2? 即以此類推進行遞歸查詢connect? by? 是遞歸查詢的標志??? 若省去prior 則僅查詢滿足start with 條件的記錄 .

?

總結

以上是生活随笔為你收集整理的ORACLE ROWNUM用法、select into与insert into区别、merge into的使用、递归查询的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。