Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Modelo Rails com chave estrangeira para si mesmo


Então o problema é que tem que haver um usuário no topo da hierarquia, um usuário para quem não há gerente (editor no seu exemplo). É por isso que a solução clássica para esse tipo de estrutura é permitir valores nulos. Você reconhece isso em seu parágrafo de encerramento:

O kicker é, se o primeiro usuário não tiver um CRIADOR ou um EDITOR, então não há "temporário":você tem que abandonar a restrição obrigatória. Se você fizer isso, o problema com a restrição de chave estrangeira recursiva desaparecerá.

A alternativa é introduzir o que Aristóteles chamou de Primeiro Motor, um Usuário cujo Criador é ele mesmo. Dada esta tabela:
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)
)
/

é bem simples criar um usuário assim:
SQL> insert into t72 values (1,1,1)
  2  /

1 row created.

SQL> commit;

Commit complete.

SQL>

Então, por que essa não é a solução canônica. Bem, isso leva a um modelo de dados um pouco maluco que pode criar estragos com consultas hierárquicas quando adicionamos mais alguns usuários.
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> 

Basicamente, o banco de dados não gosta que USERID seja seu próprio editor. No entanto, existe uma solução alternativa, que é o NOCYCLE palavra-chave (introduzida com 10g). Isso diz ao banco de dados para ignorar referências circulares na hierarquia:
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>

Aqui não importa porque os dados ainda são corretamente hierárquicos. Mas o que acontece se fizermos isso:
SQL> update t72 set editor = 7
  2  where userid = 1
  3  /

1 row updated.

SQL> 

Perdemos um relacionamento ( 1 -> 7). Podemos usar a pseudo-coluna CONNECT_BY_ISNOCYCLE para ver qual linha está circulando.
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>  

O Oracle tem muitas funcionalidades adicionais para facilitar o trabalho com dados hierárquicos em SQL puro. Está tudo na documentação. Saiba mais .