Problem polega na tym, że na szczycie hierarchii musi znajdować się użytkownik, dla którego nie ma menedżera (w twoim przykładzie redaktora). Dlatego klasycznym rozwiązaniem tego rodzaju struktury jest dopuszczenie wartości null. Potwierdzasz to w akapicie zamykającym:
Kicker polega na tym, że jeśli pierwszy użytkownik nie ma TWÓRCY ani EDYTORA, nie ma „tymczasowego”:musisz zrezygnować z obowiązkowego ograniczenia. Jeśli to zrobisz, problem z rekurencyjnym ograniczeniem klucza obcego zniknie.
Alternatywą jest wprowadzenie tego, co Arystoteles nazwał Pierwszym Poruszycielem, Użytkownikiem, którego Stwórcą jest sam. Biorąc pod uwagę tę tabelę:
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)
)
/
utworzenie takiego użytkownika jest całkiem proste:
SQL> insert into t72 values (1,1,1)
2 /
1 row created.
SQL> commit;
Commit complete.
SQL>
Dlaczego więc nie jest to rozwiązanie kanoniczne. Cóż, prowadzi to do nieco zwariowanego modelu danych, który może spowodować spustoszenie w przypadku zapytań hierarchicznych, gdy dodamy kilku dodatkowych użytkowników.
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>
Zasadniczo baza danych nie lubi USERID jako własnego edytora. Istnieje jednak obejście, którym jest NOCYCLE
słowo kluczowe (wprowadzane z 10g). Nakazuje to bazie danych ignorować odwołania cykliczne w hierarchii:
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>
Tutaj nie ma to znaczenia, ponieważ dane są nadal poprawnie hierarchiczne. Ale co się stanie, jeśli to zrobimy:
SQL> update t72 set editor = 7
2 where userid = 1
3 /
1 row updated.
SQL>
Tracimy związek ( 1 -> 7). Możemy użyć pseudokolumny CONNECT_BY_ISNOCYCLE, aby zobaczyć, który wiersz jest cykliczny.
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 ma wiele dodatkowych funkcji, które ułatwiają pracę z danymi hierarchicznymi w czystym SQL. To wszystko jest w dokumentacji. Dowiedz się więcej .