階層(再帰)クエリ

オブジェクトのツリー



再帰を理解するには、まず再帰を理解する必要があります。 おそらくそれが、再帰クエリがほとんど使用されない理由です。 確かに、SQLクエリが何であるか想像できます。再帰クエリが通常のクエリとどのように異なるかを説明します。 トピックは膨大であることが判明し、長い読書の準備ができました。 主にOracleについて説明しますが、他のDBMSも記載されています。







問題の本質





最新のDBMS(データベース管理システム)のほとんどはリレーショナルです。 行(レコード)と列(レコードのフィールド)が存在する2次元テーブルの形式でデータを表します。 しかし実際には、データの異なる組織、つまり階層構造に遭遇することがよくあります。



コンピューター上のファイルのリストを見てください。それらはすべてツリーで構成されています。 同様に、図書館の本を想像できます:図書館->ホール->キャビネット->棚->本。 サイト上の同じものと記事:サイト->セクション->サブセクション->記事。 例は長い間与えることができます。 ただし、ここでもすべてを個別のテーブルに分割できます。ライブラリのリストを格納するテーブル、ホールのリスト用の別のテーブル、キャビネット用の3つ目などです。 ただし、ネストの深さが事前にわからない場合、またはこのネストが変更される可能性がある場合は、階層から逃げることはできません。



問題は、階層構造を持つデータがリレーショナルモデルで非常に不十分に表現されることです。 SQL-92標準には、それらを処理するツールはありません。



しかし、そのようなツールはSQL-1999標準に登場しました。 確かに、それまでにOracleは独自のCONNECT BYステートメントをすでに持っていました。 それにもかかわらず、SQL-1999では、再帰クエリの構文はOracle CONNECT BY構文とは完全に異なり、WITHキーワードを使用します。 他のDBMSでの再帰クエリの実装はやや遅れていたため、MS SQL Serverにはバージョン2005でのみ登場しました。



構文と同様に、用語にも違いがあります。 Oracleでは、一般的に議論されているクエリは「階層的」と呼ばれ、他のクエリはすべて「再帰的」です。 これの本質は変わらず、両方を使用します。



言葉から行動へ!





デモンストレーションでは、ディレクトリ構造を使用します。3つのフィールドで構成されるテストテーブルが必要です。

id-識別子

pid-親識別子(同じテーブル内の別のレコードのIDを参照)、

title-ディレクトリの名前(いくつかのフィールドや他のテーブルへのリンクも含めて、ディレクトリの代わりに何でもかまいません)。



create table test_table (

  id int,

  pid int,

  title varchar(256)

);








mySQL, . , Oracle : int — number, varchar — varchar2.



. , . , , , , .



insert into test_table values (1, null, '');







, . , . -, SELECT * FROM test_table :



  ID        PID TITLE
---- ---------- --------------------
   1            
   2          1 
   3          2  "  "
   4          1 
   5          1 
   6          3  
   7          3  1
   8          3  2
   9          8  1
  10          5 




. .



mySQL





mySQL – , php ( , , , “mySQL tree” ..).



, , , . , .



- php. mySQL. , , ( ). , AJAX. pid , . , , .



SQL-1999





SQL-92, , . :-), (LOB), SIMILAR DISTINCT, , , . , .



, WITH. . :



WITH [recursive] __ [ ( ) ]
AS () 
 




MS SQL RECURSIVE, . . DB2, Sybase iAnywhere, MS SQL, 2005, , SQL 1999.



:



WITH RECURSIVE

 Rec (id, pid, title)

 AS (

   SELECT id, pid, title FROM test_table

   UNION ALL

   SELECT Rec.id, Rec.pid, Rec.title

    FROM Rec, test_table

    WHERE Rec.id = test_table.pid

   )

 SELECT * FROM Rec

 WHERE pid is null;








Rec, , test_table. Rec , : WHERE Rec.id = test_table.pid. , , pid , .. .



, MS SQL Server 2005 , . . , .



MS SQL 2008 hierarchyid. XaocCPS .



Oracle





- Oracle! . Oracle 8- , . . . , , .



: . ?



     Oracle



, – CONNECT BY, “” .



START WITH , .. ( ) . , : pid is null, id = 1, substr(title, 1, 1) = ‘’.



CONNECT BY . , . - while . , 10 : rownum<=10 – 10 . ? , – 1- . , rownum , , 1 . . .



? , PRIOR. , + -. “ ” – , . pid = PRIOR id ( PRIOR id = pid, , …).



? , START WITH, . PRIOR. , , (pid) , id . . , , .



, Oracle. , . , , - . id pid. , Oracle LEVEL. , . , 1- 1, 2, — 3 ..



SELECT level, id, pid, title

FROM test_table

START WITH pid is null

CONNECT BY PRIOR id = pid;








LEVEL         ID        PID TITLE
------ ---------- ---------- -------------------
     1          1            
     2          2          1 
     3          3          2  "  "
     4          6          3  
     4          7          3  1
     4          8          3  2
     5          9          8  1
     2          4          1 
     2          5          1 
     3         10          5 




. . , -, . , : ORDER BY title.



SELECT level, id, pid, title

FROM test_table

START WITH pid is null

CONNECT BY PRIOR id = pid

ORDER BY title;








LEVEL         ID        PID TITLE
------ ---------- ---------- -------------------
     2          2          1 
     4          6          3  
     2          5          1 
     3         10          5 
     2          4          1 
     3          3          2  "  "
     4          7          3  1
     4          8          3  2
     1          1            
     5          9          8  1




, ! . ? ( level), ORDER BY. , , SIBLINGS. ORDER SIBLINGS BY title – .



, , . “” , :



SELECT lpad(' ', 3*level)||title as Tree

FROM test_table

START WITH pid is null

CONNECT BY PRIOR id = pid

ORDER SIBLINGS BY title;








TREE
-----------------------------
   
      
          "  "
             
             1
             2
                1
      
         
      




, , .



, , : /home/maovrn/documents/ ..? . : Oracle . SYS_CONNECT_BY_PATH(). : -. , : SYS_CONNECT_BY_PATH(title, ‘/’).



, . , , WHERE. . , FROM. “ 1”, id=9:



SELECT SYS_CONNECT_BY_PATH(title, '/') as Path

FROM test_table

WHERE id=9

START WITH pid is null

CONNECT BY PRIOR id = pid;








PATH
----------------------------------------------------
/// "  "/ 2/ 1




CONNECT_BY_ISLEAF. , LEVEL. 0 1. – 0. , “”, CONNECT_BY_ISLEAF 1.



? , . PRIOR, . CONNECT_BY_ROOT, ( !) , .. .



SELECT id, pid, title, level,

 CONNECT_BY_ISLEAF as IsLeaf,

 PRIOR title as Parent,

 CONNECT_BY_ROOT title as Root

FROM test_table

START WITH pid is null

CONNECT BY PRIOR id = pid

ORDER SIBLINGS BY title;








ID PID TITLE                LEVEL LEAF PARENT               ROOT
-- --- -------------------- ----- ---- -------------------- ------
1                     1     0    
2  1                 2     0                   
3  2    "  "  3     0                  
6  3             4     1     "  "  
7  3    1               4     1     "  "  
8  3    2               4     0     "  "  
9  8    1             5     1     2               
5  1                   2     0                   
10 5              3     1                    
4  1                  2     1                   




, , Oracle . , , – , . “” NOCYCLE CONNECT BY – . . “” , CONNECT_BY_ISCYCLE – 0, , , 1.



, . ; , ( commit – ):



update test_table set pid=10 where id=5;







- , , , , . , , .. . START WITH, – . . :



SELECT CONNECT_BY_ISCYCLE as cycl, id, pid, title

FROM test_table

START WITH id=5

CONNECT BY NOCYCLE PRIOR id = pid;








CYCL         ID        PID TITLE
---- ---------- ---------- ----------
   0          5         10 
   1         10          5 








, .



, . , id 1, . . , .



. :



DELETE FROM test_table WHERE id IN (3, 5);







? -, 1 , . id , , :



SELECT max(id) FROM test_table







1 max . ! – .



SELECT rownum as rn FROM dual

 CONNECT BY level <= (SELECT max(id) FROM test_table);








“SELECT … FROM dual” , , . Dual – , . ‘X’. , .



, , , pivot. , . dual Oracle .



, , , :



SELECT sq.rn

FROM (SELECT rownum as rn FROM dual

 CONNECT BY level <= (SELECT max(id) FROM test_table)) sq

WHERE sq.rn not in (SELECT id FROM test_table)

ORDER BY rn;








  RN
----
   3
   5




. , . ROLLBACK. COMMIT!



All Articles