📜 ⬆️ ⬇️

Hierarchical (recursive) queries

Object Tree

To understand recursion, you first need to understand recursion. Perhaps that is why recursive queries are used so rarely. Surely you imagine what a SQL query is, I will tell you how recursive queries differ from ordinary ones. Subject turned voluminous, get ready for a long reading. Basically it will be about Oracle, but other DBMS are mentioned.


')

The essence of the problem



Most modern DBMSs (Database Management System) are relational, i.e. present data in the form of a two-dimensional table, in which there are rows (records) and columns (fields of records). But in practice, we often encounter a different data organization, namely hierarchical.

Take a look at the list of files on your computer: they are all organized in a tree. Similarly, you can submit books in the library: Library-> Hall-> Wardrobe-> Shelf-> Book. The same and articles on the site: Site-> Section-> Subsection-> Article. Examples can be given for a long time. However, it is still possible to divide everything into separate tables: a table for storing the list of libraries, another table for the list of halls, a third for cabinets, etc. But if the nesting depth is not known in advance, or this nesting can change, then you can’t get rid of the hierarchy.

The problem is that data that has a hierarchical structure is very poorly represented in the relational model. The SQL-92 standard does not have the means to process them.

But such tools appeared in the standard SQL-1999. True, by that time Oracle already had its own CONNECT BY operator. Despite this, in SQL-1999 the syntax of recursive queries is completely different from the syntax CONNECT BY in Oracle and uses the keyword WITH. The implementation of recursive queries in other DBMSs was somewhat delayed, as it appeared in MS SQL Server only in version 2005.

Just as in the syntax, there are differences in terminology. In Oracle, queries that are commonly discussed are called “hierarchical,” while for others, “recursive”. The essence of this does not change, I will use both.

From words to deeds!



For the demonstration we will use the directory structure, we will need a test table consisting of 3 fields:
id - id
pid is the parent ID (refers to the id of another entry in the same table),
title - the name of the directory (instead, it can be anything, even a few fields or links to other tables).

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!

Source: https://habr.com/ru/post/43955/


All Articles