乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      Oracle遞歸樹select...start with... connect by ...prior 理解

       168一路發(fā) 2010-08-04
      Oracle樹查詢的最重要的就是select...start with... connect by ...prior 語法了。依托于該語法,我們可以將一個表形結(jié)構(gòu)的中以樹的順序列出來。在下面列述了Oracle中樹型查詢的常用查詢方式以及經(jīng)常使用的與樹查詢相關的Oracle特性函數(shù)等,在這里只涉及到一張表中的樹查詢方式而不涉及多表中的關聯(lián)等。
            以我做過的一個項目中的表為例,表結(jié)構(gòu)如下:
      Sql代碼
      CREATE TABLE FLFL   
      (   
        ID      NUMBER                                NOT NULL,   
        MC      NVARCHAR2(20),   
        FLJB    NUMBER,   
        SJFLID  NUMBER   
      )  
      CREATE TABLE FLFL
      (
        ID      NUMBER                                NOT NULL,
        MC      NVARCHAR2(20),
        FLJB    NUMBER,
        SJFLID  NUMBER
      )      FLJB是作為樹的級別,在很多查詢中可以加快SQL的查詢效率。在下面演示的功能基本上不使用這個關鍵字。
            SJFLID存儲的是上級ID,如果是頂級父節(jié)點,該SJFLID為null(得補充一句,當初的確是這樣設計的,不過現(xiàn)在知道,表中最好別有null記錄,這會引起全文掃描,建議改成0代替)。
            我們從最基本的操作,逐步列出樹查詢中常見的操作,所以查詢出來的節(jié)點以家族中的輩份作比方。
            1. 查找樹中的所有頂級父節(jié)點(輩份最長的人)。 假設這個樹是個目錄結(jié)構(gòu),那么第一個操作總是找出所有的頂級節(jié)點,再根據(jù)該節(jié)點找到其下屬節(jié)點。
      Sql代碼
      SELECT * FROM flfl WHERE sjflid IS NULL;  
      SELECT * FROM flfl WHERE sjflid IS NULL;      這是個引子,沒用到樹型查詢。
            2.查找一個節(jié)點的直屬子節(jié)點(所有兒子)。 如果查找的是直屬子類節(jié)點,也是不用用到樹型查詢的。
      Sql代碼
      SELECT * FROM flfl WHERE sjflid = 819459;  
      SELECT * FROM flfl WHERE sjflid = 819459;      這個可以找到ID為819459的直屬子類節(jié)點。
            3.查找一個節(jié)點的所有 直屬子節(jié)點(所有后代)。
      Sql代碼
      SELECT * FROM flfl START WITH ID = 819459 CONNECT BY sjflid = PRIOR ID;  
      SELECT * FROM flfl START WITH ID = 819459 CONNECT BY sjflid = PRIOR ID;      這個查找的是ID為819459的節(jié)點下的所有直屬子類節(jié)點,包括子輩的和孫子輩的所有直屬節(jié)點。
            4.查找一個節(jié)點的直屬父節(jié)點(父親)。 如果查找的是節(jié)點的直屬父節(jié)點,也是不用用到樹型查詢的。
      Sql代碼
      SELECT b.* FROM flfl a JOIN flfl b ON a.sjflid = b.ID WHERE a.ID = 6758;  
      SELECT b.* FROM flfl a JOIN flfl b ON a.sjflid = b.ID WHERE a.ID = 6758;      這個找到的是ID為6758的節(jié)點的直屬父節(jié)點,要用到同一張表的關聯(lián)了。
            5.查找一個節(jié)點的所有直屬父節(jié)點(祖宗)。
      Sql代碼
      SELECT * FROM flfl START WITH ID = 6758 CONNECT BY PRIOR sjflid = ID;  
      SELECT * FROM flfl START WITH ID = 6758 CONNECT BY PRIOR sjflid = ID;      這里查找的就是ID為6758的所有直屬父節(jié)點,打個比方就是找到一個人的父親、祖父等。但是值得注意的是這個查詢出來的結(jié)果的順序是先列出子類節(jié)點再列出父類節(jié)點,姑且認為是個倒序吧。
            上面列出兩個樹型查詢方式,第3條語句和第5條語句,這兩條語句之間的區(qū)別在于prior關鍵字的位置不同,所以決定了查詢的方式不同。 當sjflid = PRIOR ID時,數(shù)據(jù)庫會根據(jù)當前的ID迭代出sjflid與該ID相同的記錄,所以查詢的結(jié)果是迭代出了所有的子類記錄;而PRIOR ID = sjflid時,數(shù)據(jù)庫會跟據(jù)當前的sjflid來迭代出與當前的sjflid相同的id的記錄,所以查詢出來的結(jié)果就是所有的父類結(jié)果。
            以下是一系列針對樹結(jié)構(gòu)的更深層次的查詢,這里的查詢不一定是最優(yōu)的查詢方式,或許只是其中的一種實現(xiàn)而已。
            6.查詢一個節(jié)點的兄弟節(jié)點(親兄弟)。
      Sql代碼
      SELECT a.*   
        FROM flfl a   
       WHERE EXISTS (SELECT *   
                       FROM flfl b   
                      WHERE a.sjflid = b.sjflid AND b.ID = 6757);  
      SELECT a.*
        FROM flfl a
       WHERE EXISTS (SELECT *
                       FROM flfl b
                      WHERE a.sjflid = b.sjflid AND b.ID = 6757);      這里查詢的就是與ID為6757的節(jié)點同屬一個父節(jié)點的節(jié)點了,就好比親兄弟了。
            7.查詢與一個節(jié)點同級的節(jié)點(族兄弟)。 如果在表中設置了級別的字段,上表中的FLJB,那么在做這類查詢時會很輕松,同一級別的就是與那個節(jié)點同級的,在這里列出不使用該字段時的實現(xiàn)!
      Sql代碼
      WITH tmp AS  
           (SELECT     a.*, LEVEL lev   
                  FROM flfl a   
            START WITH a.sjflid IS NULL  
            CONNECT BY a.sjflid = PRIOR a.ID)   
      SELECT *   
        FROM tmp   
       WHERE lev = (SELECT lev   
                      FROM tmp   
                     WHERE ID = 819394)  
      WITH tmp AS
           (SELECT     a.*, LEVEL lev
                  FROM flfl a
            START WITH a.sjflid IS NULL
            CONNECT BY a.sjflid = PRIOR a.ID)
      SELECT *
        FROM tmp
       WHERE lev = (SELECT lev
                      FROM tmp
                     WHERE ID = 819394)       這里使用兩個技巧,一個是使用了LEVEL來標識每個節(jié)點在表中的級別,還有就是使用with語法模擬出了一張帶有級別的臨時表。
            8.查詢一個節(jié)點的父節(jié)點的的兄弟節(jié)點(伯父與叔父)。
      Sql代碼
      WITH tmp AS  
           (SELECT     flfl.*, LEVEL lev   
                  FROM flfl   
            START WITH sjflid IS NULL  
            CONNECT BY sjflid = PRIOR ID)   
      SELECT b.*   
        FROM tmp b,   
             (SELECT *   
                FROM tmp   
               WHERE ID = 7004 AND lev = 2) a   
       WHERE b.lev = 1   
      UNION ALL  
      SELECT *   
        FROM tmp   
       WHERE sjflid = (SELECT DISTINCT x.ID   
                                  FROM tmp x,   
                                       tmp y,   
                                       (SELECT *   
                                          FROM tmp   
                                         WHERE ID = 7004 AND lev > 2) z   
                                 WHERE y.ID = z.sjflid AND x.ID = y.sjflid);  
      WITH tmp AS
           (SELECT     flfl.*, LEVEL lev
                  FROM flfl
            START WITH sjflid IS NULL
            CONNECT BY sjflid = PRIOR ID)
      SELECT b.*
        FROM tmp b,
             (SELECT *
                FROM tmp
               WHERE ID = 7004 AND lev = 2) a
       WHERE b.lev = 1
      UNION ALL
      SELECT *
        FROM tmp
       WHERE sjflid = (SELECT DISTINCT x.ID
                                  FROM tmp x,
                                       tmp y,
                                       (SELECT *
                                          FROM tmp
                                         WHERE ID = 7004 AND lev > 2) z
                                 WHERE y.ID = z.sjflid AND x.ID = y.sjflid);       這里查詢分成以下幾步。首先,將第7個一樣,將全表都使用臨時表加上級別;其次,根據(jù)級別來判斷有幾種類型,以上文中舉的例子來說,有三種情況:(1)當前節(jié)點為頂級節(jié)點,即查詢出來的lev值為1,那么它沒有上級節(jié)點,不予考慮。(2)當前節(jié)點為2級節(jié)點,查詢出來的lev值為2,那么就只要保證lev級別為1的就是其上級節(jié)點的兄弟節(jié)點。(3)其它情況就是3以及以上級別,那么就要選查詢出來其上級的上級節(jié)點(祖父),再來判斷祖父的下級節(jié)點都是屬于該節(jié)點的上級節(jié)點的兄弟節(jié)點。 最后,就是使用UNION將查詢出來的結(jié)果進行結(jié)合起來,形成結(jié)果集。
            9.查詢一個節(jié)點的父節(jié)點的同級節(jié)點(族叔)。
            這個其實跟第7種情況是相同的。
      Sql代碼
      WITH tmp AS  
           (SELECT     a.*, LEVEL lev   
                  FROM flfl a   
            START WITH a.sjflid IS NULL  
            CONNECT BY a.sjflid = PRIOR a.ID)   
      SELECT *   
        FROM tmp   
       WHERE lev = (SELECT lev   
                      FROM tmp   
                     WHERE ID = 819394) - 1  
      WITH tmp AS
           (SELECT     a.*, LEVEL lev
                  FROM flfl a
            START WITH a.sjflid IS NULL
            CONNECT BY a.sjflid = PRIOR a.ID)
      SELECT *
        FROM tmp
       WHERE lev = (SELECT lev
                      FROM tmp
                     WHERE ID = 819394) - 1      只需要做個級別判斷就成了。
            基本上,常見的查詢在里面了,不常見的也有部分了。其中,查詢的內(nèi)容都是節(jié)點的基本信息,都是數(shù)據(jù)表中的基本字段,但是在樹查詢中還有些特殊需求,是對查詢數(shù)據(jù)進行了處理的,常見的包括列出樹路徑等。
            補充一個概念,對于數(shù)據(jù)庫來說,根節(jié)點并不一定是在數(shù)據(jù)庫中設計的頂級節(jié)點,對于數(shù)據(jù)庫來說,根節(jié)點就是start with開始的地方。
            下面列出的是一些與樹相關的特殊需求。
            10.名稱要列出名稱全部路徑。
            這里常見的有兩種情況,一種是是從頂級列出,直到當前節(jié)點的名稱(或者其它屬性);一種是從當前節(jié)點列出,直到頂級節(jié)點的名稱(或其它屬性)。舉地址為例:國內(nèi)的習慣是從省開始、到市、到縣、到居委會的,而國外的習慣正好相反(老師說的,還沒接過國外的郵件,誰能寄個瞅瞅 )。
            從頂部開始:
      Sql代碼
      SELECT     SYS_CONNECT_BY_PATH (mc, '/')   
            FROM flfl   
           WHERE ID = 6498   
      START WITH sjflid IS NULL  
      CONNECT BY sjflid = PRIOR ID;  
      SELECT     SYS_CONNECT_BY_PATH (mc, '/')
            FROM flfl
           WHERE ID = 6498
      START WITH sjflid IS NULL
      CONNECT BY sjflid = PRIOR ID;      從當前節(jié)點開始:
      Sql代碼
      SELECT     SYS_CONNECT_BY_PATH (mc, '/')   
            FROM flfl   
      START WITH ID = 6498   
      CONNECT BY PRIOR sjflid = ID;  
      SELECT     SYS_CONNECT_BY_PATH (mc, '/')
            FROM flfl
      START WITH ID = 6498
      CONNECT BY PRIOR sjflid = ID;      在這里我又不得不放個牢騷了。oracle只提供了一個sys_connect_by_path函數(shù),卻忘了字符串的連接的順序。在上面的例子中,第一個SQL是從根節(jié)點開始遍歷,而第二個SQL是直接找到當前節(jié)點,從效率上來說已經(jīng)是千差萬別,更關鍵的是第一個SQL只能選擇一個節(jié)點,而第二個SQL卻是遍歷出了一顆樹來。再次PS一下。
            sys_connect_by_path函數(shù)就是從start with開始的地方開始遍歷,并記下其遍歷到的節(jié)點,start with開始的地方被視為根節(jié)點,將遍歷到的路徑根據(jù)函數(shù)中的分隔符,組成一個新的字符串,這個功能還是很強大的。
            11.列出當前節(jié)點的根節(jié)點。
            在前面說過,根節(jié)點就是start with開始的地方。
      Sql代碼
      SELECT     CONNECT_BY_ROOT mc, flfl.*   
            FROM flfl   
      START WITH ID = 6498   
      CONNECT BY PRIOR sjflid = ID;  
      SELECT     CONNECT_BY_ROOT mc, flfl.*
            FROM flfl
      START WITH ID = 6498
      CONNECT BY PRIOR sjflid = ID;      connect_by_root函數(shù)用來列的前面,記錄的是當前節(jié)點的根節(jié)點的內(nèi)容。
            12.列出當前節(jié)點是否為葉子。
            這個比較常見,尤其在動態(tài)目錄中,在查出的內(nèi)容是否還有下級節(jié)點時,這個函數(shù)是很適用的。
      Sql代碼
      SELECT     CONNECT_BY_ISLEAF, flfl.*   
            FROM flfl   
      START WITH sjflid IS NULL  
      CONNECT BY sjflid = PRIOR ID;  
      SELECT     CONNECT_BY_ISLEAF, flfl.*
            FROM flfl
      START WITH sjflid IS NULL
      CONNECT BY sjflid = PRIOR ID;      connect_by_isleaf函數(shù)用來判斷當前節(jié)點是否包含下級節(jié)點,如果包含的話,說明不是葉子節(jié)點,這里返回0;反之,如果不包含下級節(jié)點,這里返回1。
            至此,oracle樹型查詢基本上講完了,以上的例子中的數(shù)據(jù)是使用到做過的項目中的數(shù)據(jù),因為里面的內(nèi)容可能不好理解,所以就全部用一些新的例子來進行闡述。以上所有SQL都在本機上測試通過,也都能實現(xiàn)相應的功能,但是并不能保證是解決這類問題的最優(yōu)方案(如第8條明顯寫成存儲過程會更好),如果誰有更好的解決方案、或者有關oracle樹查詢的任何問題,歡迎留言討論,以上的SQL有什么問題也歡迎大家留言批評。
       
       

        本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
        轉(zhuǎn)藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多