Por que chaves estrangeiras compostas precisam de uma restrição exclusiva separada?

10

Aqui está uma tabela simples em que os registros podem fazer referência aos registros-pai na mesma tabela:

CREATE TABLE foo (
    id         SERIAL  PRIMARY KEY,
    parent_id  INT     NULL,
    num        INT     NOT NULL,
    txt        TEXT    NULL,
    FOREIGN KEY (parent_id) REFERENCES foo(id)
);

Com o requisito adicional de que um dos outros valores de campo ( num) deva ser idêntico entre os registros pai e filho, pensei que uma chave estrangeira composta deveria fazer o truque. Eu mudei a última linha para

    FOREIGN KEY (parent_id, num) REFERENCES foo(id, num)

e obteve ERRO: não há nenhuma restrição exclusiva correspondente às chaves fornecidas para a tabela referenciada "foo" .

Posso adicionar facilmente essa restrição, mas não entendo por que é necessário, quando uma das colunas referenciadas ( id) já é garantida como única? Do meu ponto de vista, a nova restrição seria redundante.

Zilk
fonte

Respostas:

11

É uma limitação do DBMS - em todos eles, até onde eu sei. E não apenas ao adicionar uma coluna, mas também ao reorganizar as colunas. Se tivermos uma UNIQUErestrição (a1, a2), não podemos adicionar uma FOREIGN KEYa REFERENCES (a2, a1)menos que exista uma restrição única sobre a (a2, a1)que é essencialmente redundante.

Não seria terrivelmente difícil adicionar isso como um recurso:

Quando há uma UNIQUErestrição ativada (a), qualquer combinação (a, b, c, ..., z)ou (b,c, ...a, ...z)combinação também é garantida UNIQUE.

ou a generalização:

Quando há uma UNIQUErestrição ativada (a1, a2, ..., aN), qualquer (a1, a2, ..., aN, b1, b2, ..., bM)combinação ou rearranjo também é garantido UNIQUE.

Parece que não foi solicitado ou não foi considerado alta o suficiente para ser implementado.

Você sempre pode fazer uma solicitação - no respectivo canal - para que o recurso seja implementado. Ou mesmo implemente você mesmo, se o DBMS for de código aberto, como o Postgres.

ypercubeᵀᴹ
fonte
Não tenho certeza de que seria tão simples assim. E os índices parciais ou valores NULL? etc .. NULL ainda pode funcionar bem se você estiver satisfeito NULL != NULL. Enfim .. :)
Joishi Bodio 26/10/16
@JoishiBodio Não acho que os nulos sejam um problema. Restrições UNIQUE também podem ser definidas ou colunas anuláveis. O padrão é que, se alguma coluna tiver um NULL, a restrição será passada e a linha aceita.
ypercubeᵀᴹ
No segundo, porém, se a1, a2, ... aN não são anuláveis ​​e b1, b2, bM, podemos ter problemas. Mas o recurso certamente poderia ser implementado para colunas não anuláveis. O que provavelmente é preocupante são as implicações de eficiência.
ypercubeᵀᴹ
Estou familiarizado com o local UNIQUE INDEXonde as colunas estão NULLABLE.. e foi por isso que mencionei. :) Mas eu concordo - no caso de não haver NULLs (e nem um índice parcial), provavelmente é bastante direto.
31816 Joishi Bodio
5

Chaves estrangeiras em geral (não apenas compostas) DEVEM apontar para uma CHAVE ÚNICA de algum tipo em outra tabela. Caso contrário, não haveria integridade relacional dos dados.

Isso está reclamando porque, enquanto você tem uma chave exclusiva ativada (id) .. NÃO possui uma chave exclusiva ativada (id, num) .. Portanto, no que diz respeito ao DB, o par (id, num) é não GARANTIDO para ser único. Nós, como seres humanos, podemos descobrir que será único, mas tenho certeza de que haveria um monte de código adicional que eles teriam que adicionar para tornar o Postgres inteligente o suficiente para ver que "oh, ei ... o ID deve ser único , então id, num também deve ser único "..

Eu ficaria muito surpreso se eles adicionassem esse código quando tudo que você precisa fazer é criar outro índice exclusivo nas duas colunas para corrigir o problema.

Só para esclarecer, o código que eles teriam que adicionar não seria apenas esse caso simples ... ele teria que lidar com todos os casos, mesmo aqueles onde a chave estrangeira está em mais de 4 colunas, etc. Tenho certeza a lógica seria bastante complexa.

Joishi Bodio
fonte