PostgreSQL=>遞歸查詢

PostgreSQL=>遞歸查詢html

轉載請註明源地址:http://www.cnblogs.com/funnyzpc/p/8232073.html數據庫

  距上次博客更新恰好兩週,這兩週發生了不少,好比:SFTP服務拉取數據,第三方公共平臺接口邏輯遷移新框架,新框架(Spring Cloud)上手,公司月報和審計數據獲取等等。。。,差很少都有無盡的坑,尤爲是最後者,實是折騰人啊~;牢騷歸牢騷,可是事情仍是要認真作的,。。。,就目前來看,這些對於我最大的好處就是有助於快速理解公司業務邏輯;啊哈~,扯完,從這些日子開始抽週末時間學習數據庫->PosgreSQL(我的慣稱:大象api

),遂從本節起說PostgreSQL有關的動西。數組

  記得在上一家公司的時候作過一個冷門的附加功能,就是把根據傳入的部門ID(一個List)查找部門下全部的人員,當時是Oracle數據庫配合着Mybatis來作的,中間填過兩個坑,一個是Mybatis的forach的參數個數超過1K會報錯,致使遞歸不能查詢,另外一個坑是Oracle的遞歸造型稍難,這個。。。我翻了幾篇博客寫了好幾行註釋加以理解,但願後來人能明白前人的良苦用心。因爲新買MBP 未裝Oracle環境,oracle的遞歸講解就此略過哈(◡‿◡✿)o~oracle

  首先給出一個測試表(elevel) 關於職稱級別的表,一位數的ID是最大分類(英語、計算機、會計),而後子級別的parent_id字段引用父級ID,有些級別比較籠統這裏不討論哈~:框架

testDB=> select * from elevel order by  rpad(id::varchar,5,'0');函數

  id  |         name         | parent_id 學習

------+----------------------+-----------測試

    1 | 英語                 |          spa

   11 | 英語專業四八級       |         1

  111 | 英語專業四級         |        11

  112 | 英語專業八級         |        11

   12 | 大學英語3、4、六級 |         1

  121 | 大學英語三級         |        12

  122 | 大學英語四級         |        12

  123 | 大學英語六級         |        12

    2 | 計算機               |          

   21 | NCR計算機等級        |         2

  211 | NCR計算機一級        |        21

  212 | NCR計算機二級        |        21

  213 | NCR計算機三級        |        21

  214 | NCR計算機四級        |        21

   22 | IT認證類考試         |         2

  221 | CISCO認證            |        22

  222 | ORACLE認證           |        22

    3 | 會計                 |          

   31 | 會計從業證           |         3

   32 | 會計職稱             |         3

  321 | 初級職稱(助理會計師) |        32

  322 | 中級職稱(會計師)     |        32

  323 | 高級職稱(高級職稱)   |        32

 3231 | 正高級會計師         |       323

 3232 | 副高級會計師         |       323

(25 rows)

  數據造型已經給出了,這裏我放出建表語句及測試數據

 1 -- create table 
 2 CREATE TABLE elevel  3 (  4    id          integer,  5    "name"      CHARACTER VARYING (20),  6    parent_id   integer
 7 );  8 
 9 -- insert data
10      INSERT INTO elevel (id, "name", parent_id) VALUES (1, '英語', NULL); 11      INSERT INTO elevel (id, "name", parent_id) VALUES (2, '計算機', NULL); 12      INSERT INTO elevel (id, "name", parent_id) VALUES (3, '會計', NULL); 13      INSERT INTO elevel (id, "name", parent_id) VALUES (11, '英語專業四八級', 1); 14      INSERT INTO elevel (id, "name", parent_id) VALUES (111, '英語專業四級', 11); 15      INSERT INTO elevel (id, "name", parent_id) VALUES (112, '英語專業八級', 11); 16      INSERT INTO elevel (id, "name", parent_id) VALUES (121, '大學英語三級', 12); 17      INSERT INTO elevel (id, "name", parent_id) VALUES (122, '大學英語四級', 12); 18      INSERT INTO elevel (id, "name", parent_id) VALUES (12, '大學英語3、4、六級', 1); 19      INSERT INTO elevel (id, "name", parent_id) VALUES (123, '大學英語六級', 12); 20      INSERT INTO elevel (id, "name", parent_id) VALUES (21, 'NCR計算機等級', 2); 21      INSERT INTO elevel (id, "name", parent_id) VALUES (22, 'IT認證類考試', 2); 22      INSERT INTO elevel (id, "name", parent_id) VALUES (211, 'NCR計算機一級', 21); 23      INSERT INTO elevel (id, "name", parent_id) VALUES (212, 'NCR計算機二級', 21); 24      INSERT INTO elevel (id, "name", parent_id) VALUES (213, 'NCR計算機三級', 21); 25      INSERT INTO elevel (id, "name", parent_id) VALUES (214, 'NCR計算機四級', 21); 26      INSERT INTO elevel (id, "name", parent_id) VALUES (221, 'CISCO認證', 22); 27      INSERT INTO elevel (id, "name", parent_id) VALUES (222, 'ORACLE認證', 22); 28      INSERT INTO elevel (id, "name", parent_id) VALUES (31, '會計從業證', 3); 29      INSERT INTO elevel (id, "name", parent_id) VALUES (32, '會計職稱', 3); 30      INSERT INTO elevel (id, "name", parent_id) VALUES (321, '初級職稱(助理會計師)', 32); 31      INSERT INTO elevel (id, "name", parent_id) VALUES (322, '中級職稱(會計師)', 32); 32      INSERT INTO elevel (id, "name", parent_id) VALUES (323, '高級職稱(高級職稱)', 32); 33      INSERT INTO elevel (id, "name", parent_id) VALUES (3231, '正高級會計師', 323); 34      INSERT INTO elevel (id, "name", parent_id) VALUES (3232, '副高級會計師', 323); 35      COMMIT;

  如今我定一個需求:查詢「會計」(id=3)類別下的全部的子記錄(包含id=3的記錄)

1 WITH RECURSIVE le (id,name,parent_id) as 
2 ( 3  select id,name,parent_id from elevel where id=3
4  union all
5  select e2.id,e2.name,e2.parent_id from elevel e2,le e3 where e3.id=e2.parent_id 6 ) 7  select * from le order by rpad(id::varchar,5,'0') asc;

查詢結果: 

  id  |         name         | parent_id 

------+----------------------+-----------

    3 | 會計                 |          

   31 | 會計從業證           |         3

   32 | 會計職稱             |         3

  321 | 初級職稱(助理會計師) |        32

  322 | 中級職稱(會計師)     |        32

  323 | 高級職稱(高級職稱)   |        32

 3231 | 正高級會計師         |       323

 3232 | 副高級會計師         |       323

(8 rows)

根據以上查詢結果,這裏敲黑板,劃重點

  =>「RECURSIVE」 是PostgreSQL的關鍵字不是具體存在的表

  =>第一行中的:"(id,name,parent_id)"定義的是虛擬el表的參數,字段的名稱可隨意,但字段的個數必定要與3~5行中的查詢結果的個數一致!

  =>"el"是聲明的虛擬表,每次遞歸一層後都會將本層數據寫入el中

  =>第三行中的id=3是須要查詢開始層的ID,關鍵是第五行=>須要將虛擬表「el"表與「elevel」實體表連表查詢

  =>特別須要注意的是第三行中的中的where條件(e3.id=e2.parent_id) ,取虛擬表的ID和實體表parent_id連

    這個條件決定了當前遞歸查詢的查詢方式(向上查詢仍是向下查詢);

  =>第三行的遞歸開始查詢不可缺乏,否則查詢報錯,我的理解這是PostgreSQL根據首行的記錄來遞歸子記錄

好了,須要總結的大概就是這些,至於第七行中的rpad函數是向右補齊的函數,用於排序的須要,讀者能夠略去order by以後的內容。

  好了,一個簡單的遞歸查詢就成了,嗯。。。,如需求同窗說:我須要將每條記錄的遞歸結構(path)和層級(depath)的順序都顯示出來。

  遺憾的是PG遞歸查詢自己並無提供相應的函數和關鍵字來方便咱們的需求,怎麼辦=>加字段:

1 with RECURSIVE le (id,name,parent_id,path,depath) as 
2 ( 3  select id,name,parent_id,Array[id] as path,1 as depath from elevel where id=3
4  union all
5  select e2.id,e2.name,e2.parent_id,e3.path||e3.id,e3.depath+1 
6      from elevel e2,le e3 where e3.id=e2.parent_id 7 ) 8 select * from le order by rpad(id::varchar,5,'0') asc;

查詢結果:


  id  |         name         | parent_id |     path     | depath 

------+----------------------+-----------+--------------+--------

    3 | 會計                 |           | {3}          |      1

   31 | 會計從業證           |         3 | {3,3}        |      2

   32 | 會計職稱             |         3 | {3,3}        |      2

  321 | 初級職稱(助理會計師) |        32 | {3,3,32}     |      3

  322 | 中級職稱(會計師)     |        32 | {3,3,32}     |      3

  323 | 高級職稱(高級職稱)   |        32 | {3,3,32}     |      3

 3231 | 正高級會計師         |       323 | {3,3,32,323} |      4

 3232 | 副高級會計師         |       323 | {3,3,32,323} |      4

(8 rows)

  嗯~,能夠看到查詢SQL與之上的查詢不一樣的是第三行中定義了一個"Array[id]" 的遞歸結構字段,最爲和一個「1」 的深度字段,Array函數是PostgreSQL特有的數組函數,讀者能夠自行查閱資料瞭解哈( ^)o(^ )~。

  固然以上查詢語句知足既已有的需求,想下->若是這裏變我最成最初我作過的那個需求(查詢部門下的全部人,不含部門記錄),該怎麼辦呢。

  額~,遞歸自己提供給咱們的結果已經趨於完美了,因爲官方api並無提供進一步的方法,這裏只有從查詢結果着手解決這個問題囖~

with RECURSIVE le (id,name,parent_id,path,depath) as ( select id,name,parent_id,Array[id] as path,1 as depath from elevel where id=3
 union all
 select e2.id,e2.name,e2.parent_id,e3.path||e3.id,e3.depath+1 
     from elevel e2,le e3 where e3.id=e2.parent_id ) select * from le l where 0=(select count(1) from le where parent_id=l.id) order by rpad(id::varchar,5,'0') asc;

查詢結果:

  id  |         name         | parent_id |     path     | depath 

------+----------------------+-----------+--------------+--------

   31 | 會計從業證           |         3 | {3,3}        |      2

  321 | 初級職稱(助理會計師) |        32 | {3,3,32}     |      3

  322 | 中級職稱(會計師)     |        32 | {3,3,32}     |      3

 3231 | 正高級會計師         |       323 | {3,3,32,323} |      4

 3232 | 副高級會計師         |       323 | {3,3,32,323} |      4

(5 rows)

 根據以上查詢SQL來看,答案其實很簡單,在遞歸完成後將存在子記錄的用where條件過濾掉便可(見查詢語句最後一行)

嗯,以上幾個例子所有是向下遞歸查詢,下面我展現下向上查詢的語句,很簡單=>

 

1  with RECURSIVE le (id,name,parent_id) as 
2  ( 3   select id,name,parent_id from elevel where id=323
4   union all
5   select e2.id,e2.name,e2.parent_id from elevel e2,le e3 where e3.parent_id=e2.id 6  ) 7  select * from le order by rpad(id::varchar,5,'0') asc;

查詢結果:

 id  |        name        | parent_id 

-----+--------------------+-----------

   3 | 會計               |          

  32 | 會計職稱            |        3

 323 | 高級職稱(高級職稱)   |        32

能夠看到與向上查詢的查詢語句相差不幾,關鍵,關鍵是=>第5行的where條件,很意外吧,如此小的改動就有查詢方向上的變化,我的對此的理解是:

  =>遞歸向下查詢是用虛擬表的id去聯結遞歸表的parent_id

  =>遞歸向上查詢是用虛擬表的parent_id去聯結遞歸表的id

  本人愚鈍,目前對於二者的區別發現僅限於此,歡迎讀者點撥哈。。。~

最後,須要說明的是,在公司業務知足的狀況下儘量用單層查詢語句查詢,尤爲對於層級較少較固定的結構下較爲合適,此建議主要針對的是遞歸的兩大問題而言:

  1>遞歸的查詢效率較低,尤爲是記錄較多層級龐大的記錄

  2>若現有記錄的層級若有交叉,極容易致使遞歸死循環,這點尤爲要注意

  

OK, 本節完成,下節開始講:「窗口函數

如今是:2018-01-21 21:20:50,願各位晚安,明天要上班哦~

相關文章
相關標籤/搜索