Le problème est donc qu'il doit y avoir un utilisateur au sommet de la hiérarchie, un utilisateur pour lequel il n'y a pas de gestionnaire (éditeur dans votre exemple). C'est pourquoi la solution classique à ce type de structure est d'autoriser les valeurs nulles. Vous le reconnaissez dans votre dernier paragraphe :
Le plus important est que si le premier utilisateur n'a pas de CRÉATEUR ou d'ÉDITEUR, il n'y a pas de "temporaire":vous devez abandonner la contrainte obligatoire. Si vous faites cela, le problème avec la contrainte de clé étrangère récursive disparaîtra.
L'alternative est d'introduire ce qu'Aristote appelait un Premier Moteur, un Utilisateur dont le Créateur est lui-même. Etant donné ce tableau :
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)
)
/
c'est assez simple de créer un tel utilisateur :
SQL> insert into t72 values (1,1,1)
2 /
1 row created.
SQL> commit;
Commit complete.
SQL>
Alors pourquoi n'est-ce pas la solution canonique. Eh bien, cela conduit à un modèle de données un peu farfelu qui peut créer des ravages avec les requêtes hiérarchiques une fois que nous avons ajouté quelques utilisateurs supplémentaires.
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>
Fondamentalement, la base de données n'aime pas que USERID soit son propre éditeur. Cependant, il existe une solution de contournement, qui est le NOCYCLE
mot-clé (introduit avec 10g). Cela indique à la base de données d'ignorer les références circulaires dans la hiérarchie :
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>
Ici, cela n'a pas d'importance car les données sont toujours correctement hiérarchisées. Mais que se passe-t-il si nous faisons ceci :
SQL> update t72 set editor = 7
2 where userid = 1
3 /
1 row updated.
SQL>
Nous perdons une relation ( 1 -> 7). Nous pouvons utiliser la pseudo-colonne CONNECT_BY_ISNOCYCLE pour voir quelle ligne est en cycle.
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 dispose de nombreuses fonctionnalités supplémentaires pour faciliter le travail avec des données hiérarchiques en SQL pur. Tout est dans la documentation. En savoir plus .