Quindi il problema è che ci deve essere un utente in cima alla gerarchia, un utente per il quale non esiste un manager (editor nel tuo esempio). Ecco perché la soluzione classica a questo tipo di struttura è consentire valori nulli. Lo riconosci nel paragrafo di chiusura:
Il kicker è che se il primo utente non ha un CREATOR o un EDITOR allora non c'è "temporaneo":devi abbandonare il vincolo obbligatorio. Se si esegue questa operazione, il problema con il vincolo di chiave esterna ricorsiva scomparirà.
L'alternativa è introdurre quello che Aristotele chiamava un Primo Motore, un Utente il cui Creatore è se stesso. Data questa tabella:
create table t72
( userid number not null
, creator number not null
, editor number not null
, constraint t72_pk primary key (userid)
, constraint t72_cr_fk foreign key (creator)
references t72 (userid)
, constraint t72_ed_fk foreign key (editor)
references t72 (userid)
)
/
è abbastanza semplice creare un utente del genere:
SQL> insert into t72 values (1,1,1)
2 /
1 row created.
SQL> commit;
Commit complete.
SQL>
Allora perché non è questa la soluzione canonica. Bene, porta a un modello di dati leggermente stravagante che può creare scompiglio con le query gerarchiche una volta che aggiungiamo qualche altro utente.
SQL> select lpad(' ', level-1)|| u.userid as userid
2 , u.name
3 , u.editor
4 from t72 u
5 connect by
6 prior userid = editor
7 start with userid=1
8 /
ERROR:
ORA-01436: CONNECT BY loop in user data
no rows selected
SQL>
Fondamentalmente al database non piace che USERID sia il proprio editor. Tuttavia, esiste una soluzione alternativa, che è il NOCYCLE
parola chiave (introdotta con 10g). Questo dice al database di ignorare i riferimenti circolari nella gerarchia:
SQL> select lpad(' ', level-1)|| u.userid as userid
2 , u.name
3 , u.editor
4 from t72 u
5 connect by nocycle
6 prior userid = editor
7 start with userid=1
8 /
USERID NAME EDITOR
---------- ---------- ----------
1 ONE 1
2 TWO 1
3 THREE 2
4 FOUR 2
5 FIVE 2
6 SIX 2
7 SEVEN 6
7 rows selected.
SQL>
Qui non importa perché i dati sono ancora correttamente gerarchici. Ma cosa succede se facciamo questo:
SQL> update t72 set editor = 7
2 where userid = 1
3 /
1 row updated.
SQL>
Perdiamo una relazione ( 1 -> 7). Possiamo usare la pseudo-colonna CONNECT_BY_ISNOCYCLE per vedere quale riga sta scorrendo.
SQL> select lpad(' ', level-1)|| u.userid as userid
2 , u.name
3 , u.editor
4 , connect_by_iscycle
5 from t72 u
6 connect by nocycle
7 prior userid = editor
8 start with userid=1
9 /
USERID NAME EDITOR CONNECT_BY_ISCYCLE
---------- ---------- ---------- ------------------
1 ONE 7 0
2 TWO 1 0
3 THREE 2 0
4 FOUR 2 0
5 FIVE 2 0
6 SIX 2 0
7 SEVEN 6 1
7 rows selected.
SQL>
Oracle ha molte funzionalità aggiuntive per semplificare il lavoro con i dati gerarchici in puro SQL. È tutto nella documentazione. Scopri di più .