A linguagem SQL > DDL > Tabelas > Restrição FOREIGN KEY
Restrição FOREIGN KEY tl_logo2.jpg

Neste módulo vamos ver o seguinte:


O que é uma FOREIGN KEY e como a definir

A restrição FOREIGN KEY permite definir um relacionamento entre duas tabelas, sendo as respectivas regras mantidas e validadas pela base de dados.

Vamos começar por analisar o diagrama Entidade-Relacionamento que descreve as tabelas que estamos a considerar no nosso exemplo de trabalho:

EquipaJogadorJogos.jpg

Em primeiro lugar vamos considerar o relacionamento entre JOGADOR e EQUIPA. Uma equipa possui vários jogadores, sendo que cada jogador joga numa única equipa num determinado momento no tempo. Se adicionarmos a dimensão tempo ao nosso problema, então o jogador joga em várias equipas ao longo do tempo, o que dá ao relacionamento a cardinalidade N para N. Este conceito temporal introduz a noção de "contrato" entre jogador e equipa: um jogador é contratado por um período de tempo.

Em segundo lugar vamos considerar o relacionamento entre EQUIPA e JOGO. Um jogo envolve duas equipas: a visitante e a visitada. Isto traduz-se num duplo relacionamento entre as duas entidades: uma relação define a equipa da casa enquanto a outra define a equipa visitante.

O script apresentado abaixo implementa os relacionamentos descritos no ponto anterior:

create table EQUIPA  (
   ID_EQUIPA   NUMBER(10,0)                             not null,
   NOME        VARCHAR2(100) constraint nn_equipa_nome  not null,
   constraint PK_EQUIPA primary key (ID_EQUIPA)
);

create table JOGADOR  (
   ID_JOGADOR           NUMBER(10,0)                    not null,
   NOME                 varchar2(100)                   not null,
   POSICAO              varchar2(100)                   not null,
   BI                   VARCHAR(100),
   constraint PK_JOGADOR primary key (ID_JOGADOR),
   constraint UK_JOGADOR unique (BI)
);

create table JOGO  (
   EQUIPA_CASA          NUMBER(10,0)                    not null,
   EQUIPA_VISITANTE     NUMBER(10,0)                    not null,
   DATA                 DATE                            not null,
   LOCAL                VARCHAR2(100)                  default 'Estádio Alvalade XXI' not null,
   RISCO_SEGURANCA      VARCHAR(100)                    not null,
   constraint PK_JOGO primary key (EQUIPA_CASA, EQUIPA_VISITANTE, DATA),
   constraint FK_JOGO_REF_EQUIPA_CASA foreign key (EQUIPA_CASA)
         references EQUIPA (ID_EQUIPA),
   constraint FK_JOGO_REF_EQUIPA_VISITANTE foreign key (EQUIPA_VISITANTE)
         references EQUIPA (ID_EQUIPA),
   constraint CK_JOGO_01 check (risco_seguranca in ('Alto','Medio','Baixo') and equipa_casa != equipa_visitante)
);

create table CONTRATADO  (
   ID_JOGADOR           NUMBER(10,0)                    not null,
   ID_EQUIPA            NUMBER(10,0)                    not null,
   DATA_INICIO          DATE                            not null,
   DATA_FIM             DATE                            not null,
   constraint PK_CONTRATADO primary key (ID_JOGADOR, ID_EQUIPA, DATA_INICIO),
   constraint FK_CONTRATA_REFERENCE_JOGADOR foreign key (ID_JOGADOR)
         references JOGADOR (ID_JOGADOR),
   constraint FK_CONTRATA_REFERENCE_EQUIPA foreign key (ID_EQUIPA)
         references EQUIPA (ID_EQUIPA),
   constraint CKT_CONTRATADO check (data_inicio < data_fim)
);

Como se pode verificar a restrição FK_CONTRATA_REFERENCE_JOGADOR define que a coluna ID_JOGADOR da tabela CONTRATADO referencia a coluna com o mesmo nome da tabela JOGADOR. Isto define a coluna ID_JOGADOR em CONTRATADO como uma FOREIGN KEY, o que em português se costuma traduzir por chave externa ou estrangeira. A partir daqui vamos designar a tabela JOGADOR como MASTER deste relacionamento e a tabela CONTRATADO como SLAVE. A criação desta restrição obriga a base de dados a executar um conjunto de validações:

A restrição FK_CONTRATA_REFERENCE_EQUIPA define a coluna ID_EQUIPA da tabela CONTRATADO como FOREIGN KEY da coluna com o mesmo nome na tabela EQUIPA o que activa as regras de integridade referencial entre essas duas colunas.

Na tabela JOGO temos duas FOREIGN KEYS: uma define a equipa da casa, enquanto a outra define a equipa visitante. As regras de integridade referencial garantem que um JOGO se realiza entre equipas que existem, ou seja, que estão presentes na tabela EQUIPA. Esta regra não garante que o jogo se realiza entre duas equipas diferentes, o que é assegurado por uma restrição CHECK.

Uma base de dados Oracle tem as seguintes regras na definição de FOREIGN KEYS:

A criação de restrição FOREIGN KEY pode ser feita usando sintaxe de coluna ou de tabela, como descrito no quadro abaixo:

Tipo Sintaxe
Coluna
(só é abrangida uma coluna)
CONSTRAINT nome REFERENCES tabela (coluna)
Tabela
(são abrangidas várias colunas)
, CONSTRAINT nome FOREIGN KEY (col1, col2,...) REFERENCES tabela (coluna1, coluna2, ...);
Notar que col1 e col2 são os nomes de colunas da tabela que está a ser criada ou alterada, enquanto coluna1 e coluna2 são referências a outra tabela (ou à própria tabela num outro contexto).

O diagrama abaixo mostra um relacionamento que é possível definir entre as tabelas EMP e DEPT:

RelEmpDept.jpg

O query abaixo efectua uma consulta ao dicionário de dados para validar se o relacionamento entre EMP e DEPT foi efectivamente criado:

select *
from user_constraints
where table_name in ('EMP','DEPT');
OWNER                          CONSTRAINT_NAME                CONSTRAINT_TYPE TABLE_NAME                     SEARCH_CONDITION R_OWNER                        R_CONSTRAINT_NAME              DELETE_RULE STATUS   DEFERRABLE     DEFERRED  VALIDATED     GENERATED      BAD RELY LAST_CHANGE               INDEX_OWNER                    INDEX_NAME                     INVALID VIEW_RELATED   
------------------------------ ------------------------------ --------------- ------------------------------ ---------------- ------------------------------ ------------------------------ ----------- -------- -------------- --------- ------------- -------------- --- ---- ------------------------- ------------------------------ ------------------------------ ------- -------------- 
HR                             SYS_C004034                    C               EMP                            "EMPNO" IS NOT NULL                                                                           ENABLED  NOT DEFERRABLE IMMEDIATE VALIDATED     GENERATED NAME          06.04.08                                                                                                       

1 rows selected

O resultado do query mostra que a única restrição existente nas duas tabelas é NOT NULL sobre a coluna EMPNO de EMP. Isto significa que não existe FOREIGN KEY em EMP e que se a tentarmos criar não temos nem UNIQUE nem PRIMARY KEY em DEPT para estabelecer a relação. Para criar o relacionamento teremos que fazer o seguinte:

  1. Criar uma PRIMARY KEY em DEPT sobre a coluna DEPTNO;
  2. Criar uma FOREIGN KEY em EMP que referencie a PK criada no passo anterior;

O comando abaixo implementa os dois passos descritos acima:

alter table DEPT modify (DEPTNO constraint PK_DEPT primary key);
alter table EMP modify (DEPTNO constraint fk_emp_ref_dept references DEPT(DEPTNO));
alter table dept succeeded.

alter table emp succeeded.

No comando anterior usámos sintaxe de coluna. Na sequência de comandos abaixo removemos a restrição criada no passo anterior e criamos outra equivalente, agora usando a sintaxe de tabela:

alter table emp drop constraint fk_emp_ref_dept;
alter table emp add constraint fk_emp_ref_dept foreign key (deptno) references dept(deptno);
alter table emp succeeded.

alter table emp succeeded.

O query abaixo confirma a criação do relacionamento:

select *
from user_constraints
where table_name in ('EMP','DEPT');
OWNER                          CONSTRAINT_NAME                CONSTRAINT_TYPE TABLE_NAME                     SEARCH_CONDITION    R_OWNER                        R_CONSTRAINT_NAME              DELETE_RULE STATUS   DEFERRABLE     DEFERRED  VALIDATED     GENERATED      BAD RELY LAST_CHANGE               INDEX_OWNER                    INDEX_NAME                     INVALID VIEW_RELATED   
------------------------------ ------------------------------ --------------- ------------------------------ ------------------- ------------------------------ ------------------------------ ----------- -------- -------------- --------- ------------- -------------- --- ---- ------------------------- ------------------------------ ------------------------------ ------- -------------- 
HR                             PK_DEPT                        P               DEPT                                                                                                                         ENABLED  NOT DEFERRABLE IMMEDIATE VALIDATED     USER NAME               06.07.03                  HR                             PK_DEPT                                               
HR                             FK_EMP_REF_DEPT                R               EMP                                                HR                             PK_DEPT                        NO ACTION   ENABLED  NOT DEFERRABLE IMMEDIATE VALIDATED     USER NAME               06.07.03                                                                                                       
HR                             SYS_C004034                    C               EMP                            "EMPNO" IS NOT NULL                                                                           ENABLED  NOT DEFERRABLE IMMEDIATE VALIDATED     GENERATED NAME          06.04.08                                                                                                       

3 rows selected

note04.gif

bthome.gifTopo


Relacionamento de uma tabela com ela própria

Como foi visto nos queries hierárquicos e na junção de uma tabela com ela própria é possível definir um relacionamento entre uma tabela e ela própria. O diagrama abaixo mostra que um empregado pode ter um MANAGER (MGR), correspondendo este a outra linha da própria tabela EMP.

relachimself.jpg

O relacionamento é criado entre as colunas MGR e EMPNO, mas obriga a considerar EMPNO como PRIMARY KEY da tabela EMP. Esta tabela pode ser vista em dois contextos: a tabela dos empregados e a tabela dos "managers". O relacionamento é definido entre os dois contextos, que afinal são a mesma tabela. Os dois comandos estão descritos no exemplo abaixo:

alter table EMP modify (EMPNO constraint PK_EMP primary key);
alter table EMP modify (mgr constraint FK_EMP_REF_EMP references EMP(EMPNO));
alter table EMP succeeded.

alter table EMP succeeded.

bthome.gifTopo


Not null e diagrama entidade relacionamento

No relacionamento anterior um empregado pode não ter um "manager", como é o caso de KING. Isto traduz-se em termos de modelo pelo circulo no lado da cardinalidade 1 (zero ou um) e na implementação física pelo facto da coluna MGR suportar valores NULL. Se olharmos para o relacionamento entre EMP e DEPT vemos que o DEPTNO também suporta valores NULL, pelo que temos um circulo do lado de DEPT.

relachimself2.jpg

RelEmpDept2.jpg

Se na coluna DEPTNO de EMP adicionarmos a restrição NOT NULL, como no comando abaixo, o modelo passará ser representado como na figura abaixo:

alter table EMP modify (DEPTNO constraint NN_EMP_DEPTNO not null);
alter table EMP succeeded.

RelEmpDept3.jpg

bthome.gifTopo


Remoção em cascata

A opção ON DELETE CASCADE pode ser adiciona à restrição FOREIGN KEY e permite que, quando uma linha da tabela MASTER é removida, todas as linhas associadas na tabela SLAVE são apagadas em "cascata", mantendo a base de dados a integridade referencial. Pelo perigo que representa uma remoção em "cascata" esta opção deve ser usada com muito cuidado.

O comando abaixo remove a restrição FK_EMP_REF_DEPT e recria-a usando a opção ON DELETE CASCADE:

alter table EMP drop constraint fk_emp_ref_dept;
alter table emp add constraint fk_emp_ref_dept foreign key (deptno) references dept(deptno) on delete cascade;

bthome.gifTopo


Realizado por Turtle Learning ®. Última alteração em 2011-08-14