Prática inadequada de chave estrangeira anulável?

114

Digamos que você tenha uma tabela Pedidos com uma chave estrangeira para um ID do cliente. Agora, suponha que você queira adicionar um pedido sem um ID de cliente, (se isso deveria ser possível é outra questão), você teria que tornar a chave estrangeira NULL ... Isso é uma prática ruim ou você prefere trabalhar com uma tabela de links entre Pedidos e clientes? Embora o relacionamento seja de 1 para n, uma tabela de links tornaria n para n. Por outro lado, com uma tabela de links, não tenho mais esses NULLS ...

Na verdade, não haverá muitos NULLs no banco de dados, porque um registro com uma chave estrangeira para NULL é apenas temporariamente até que um cliente para o pedido seja adicionado.

(No meu caso não é um Pedido e um Cliente).

EDIT: Que tal um cliente não atribuído para vincular?

Lieven Cardoen
fonte
9
Esse é um dos principais objetivos de ter NULLs disponíveis em um esquema de banco de dados. Além disso, é por isso que você pode declarar os campos NULL ou NOT NULL, para que os requisitos específicos de seu esquema sejam atendidos.
gahooa
7
Inicialmente li a pergunta como chaves primárias anuláveis e estava prestes a dar um conselho forte ... :-)
Andrzej Doyle

Respostas:

51

Ter a tabela de links provavelmente é a melhor opção. Pelo menos não viola a normalização BCNF (forma normal de Boyce-Codd). no entanto, eu preferiria ser pragmático. Se você tiver poucos desses valores nulos e eles forem apenas temporários, acho que você deve pular a tabela de links, pois isso só adiciona complexidade ao esquema.

Em uma nota lateral; usar uma tabela de link não necessariamente torna n para n, se você na tabela de link usar a chave estrangeira que está apontando para sua tabela de pedidos como a chave primária nessa tabela de link, o relacionamento ainda é 1..n. Só pode haver uma entrada nessa tabela de links por pedido.

Patrik Hägne
fonte
2
source__destination_link ou SourceDestination
Svisstack
7
Eu estaria interessado em ouvir sobre uma situação em que ter uma tabela de links é melhor, nunca me deparei com uma situação em que isso teria melhorado o fluxo do processo de alguma forma.
Reimius,
5
Conforme apontado em minha resposta, eu seria pragmático neste caso específico e não usaria uma tabela de links. Tenho certeza de que os formulários normais não foram inventados para melhorar o fluxo do processo, mas para garantir consistência e evitar redundância. É uma discussão muito geral, porém, acho que deve ser vista caso a caso.
Patrik Hägne
110

Não Não há nada de errado com FKs anuláveis. Isso é comum quando a entidade para a qual o FK aponta está em um relacionamento (zero ou um) a (1 ou muitos) com a tabela referenciada por chave primária.

Um exemplo poderia ser se você tivesse um endereço físico e um atributo (coluna) de endereço para correspondência em uma tabela, com FKs para uma tabela de endereços. Você pode tornar o endereço físico anulável para manipulação quando a entidade tem apenas uma caixa postal (endereço de correspondência) e o endereço de correspondência anulável para manipulação quando o endereço de correspondência é o mesmo que o endereço físico (ou não).

Charles Bretana
fonte
38

As colunas anuláveis ​​podem estar em 1NF a 5NF, mas não em 6NF de acordo com o que li.

Somente se você souber melhor do que Chris Date "o que a primeira forma normal realmente significa". Se x e y são anuláveis ​​e, de fato, em alguma linha x e y são ambos null, então WHERE x=ynão resulta true. Isso prova, sem qualquer dúvida razoável, que nulo não é um valor (porque qualquer valor real é sempre igual a si mesmo). E como o RM prescreve que "deve haver um valor em cada célula de uma tabela", qualquer coisa que possivelmente contenha nulos não é uma coisa relacional e, portanto, a questão de 1NF nem mesmo surge.

Eu ouvi argumentar que as colunas anuláveis ​​em geral quebram o primeiro grau de normalização.

Veja acima a razão sólida subjacente a esse argumento.

Mas na prática é muito prático.

Somente se você for imune às dores de cabeça que geralmente causam no resto do mundo. Uma dessas dores de cabeça (e é apenas uma pequena, comparativamente a outros nullfenômenos) é o fato de que WHERE x=yem SQL realmente significa WHERE x is not null and y is not null and x=y, mas a maioria dos programadores simplesmente não está ciente desse fato e apenas o lê. Às vezes sem nenhum dano, outras vezes não.

Na verdade, colunas anuláveis ​​violam uma das regras de design de banco de dados mais fundamentais: não combine elementos de informação distintos em uma coluna. Os nulos fazem exatamente isso porque combinam o valor booleano "este campo está / não está realmente presente" com o valor real.

Erwin Smout
fonte
17
+1 para "WHERE x não é nulo ey não é nulo e x = y". Não estava ciente disso.
RobM de
1
Argumentos e exemplos muito bem apresentados.
pedz
1
Um problema. Quando o valor "não existe" (que É um cenário do mundo real), e o atributo do banco de dados não permite nulos, qualquer valor que esteja no atributo é ERRADO. Quanto às dores de cabeça, lembre-se, KISS, não significa apenas mantê-lo simples, significa mantê-lo o mais simples possível, mas não mais simples. Se o "modelo relacional" exigir um resultado irreal e estúpido, talvez as regras precisem ser expandidas para lidar com os dados necessários do mundo real.
Charles Bretana
1
Foi demonstrado que uma lógica de três valores leva à necessidade de uma lógica de quatro valores, e isso leva à necessidade de uma lógica de cinco valores, etc. etc. A lógica de dois valores é suficiente, mas as estruturas de dados que obtemos quando aplicá-lo torna "tão simples quanto possível" ainda muito menos simples do que "tão simples quanto desejaríamos".
Erwin Smout
2
Chris Date, Logic & Databases, Capítulo 6, "Por que a lógica do DBMS relacional não deve ter muitos valores", página 145. A lista de referências a esse capítulo também deve ser interessante, especialmente aquelas envolvendo McGoveran.
Erwin Smout
13

Não consigo ver nada de errado nisso, é apenas um relacionamento n-1 opcional que será representado com um nulo na chave estrangeira. Do contrário, se você colocar sua tabela de links, terá que cuidar para que não se torne um relacionamento nn, causando ainda mais problemas.

pedromarce
fonte
2
Na verdade, é um relacionamento 0-N, não um relacionamento 1-N opcional. Mas eu concordo com você.
Eric J.
5
Gerir? É uma restrição UNIQUE simples no lado 0 para 1!
semana
2
Sim, é uma restrição ÚNICA, mas você também terá que lidar com possíveis exceções mais tarde em seu código devido a essa restrição ...
pedromarce
4

Relacionamentos opcionais são definitivamente possíveis no modelo relacional.

Você pode usar nulos para expressar a ausência de um relacionamento. Eles são convenientes, mas causarão as mesmas dores de cabeça que os nulos causam em outros lugares. Um lugar onde eles não causam problemas são as junções. As linhas que possuem um valor nulo na chave estrangeira não correspondem a nenhuma linha na tabela referenciada. Então, eles saem de uma junção interna. Se você fizer junções externas, lidará com nulos de qualquer maneira.

Se você realmente deseja evitar nulos (6ª forma normal), você pode decompor a tabela. Uma das duas tabelas decompostas possui duas colunas de chave estrangeira. Uma é a chave estrangeira opcional que você possui e a outra é uma chave estrangeira que faz referência à chave primária da tabela original. Agora você tem que usar restrições para evitar que o relacionamento se torne muitos para muitos, se você quiser evitar isso.

Walter Mitty
fonte
2

Usar NULL seria uma boa maneira de limpar pedidos incompletos:

SELECT * FROM `orders`
WHERE `started_time` < (UNIX_TIMESTAMP() + 900) AND `customer_id` IS NULL

O texto acima mostraria pedidos com mais de 15 minutos sem um ID de cliente relacionado.

Matpie
fonte
1

Se você estiver apenas adicionando o pedido temporariamente sem ID de cliente até que um cliente seja definido, não seria mais simples adicionar o cliente e o pedido em uma única transação, removendo assim a necessidade da entrada de chave estrangeira NULL e evitando quaisquer restrições ou gatilhos você configurou ser violado?

Normalmente, essa situação surge em aplicativos web onde o pedido é detalhado antes que o cliente defina quem ele é. E nessas situações o pedido é mantido no estado de servidor ou em um cookie até que todo o estado necessário para um pedido completo seja fornecido, momento em que o pedido é mantido no banco de dados.

Chaves estrangeiras NULL são aceitáveis ​​para coisas como endereços, conforme mencionado acima. Mas um campo de cliente NULL não faz sentido para um pedido e deve ser restrito.

Mark Green
fonte
O pedido-cliente foi um exemplo. No meu aplicativo é idd mais como endereços. Não foi possível encontrar imediatamente um exemplo que estivesse totalmente correto. THX.
Lieven Cardoen
1
Este poderia ser um cenário válido se o banco de dados estivesse sendo usado para armazenar itens em um carrinho de compras, onde o carrinho de compras não pertence a um usuário registrado.
Johnie Karr
1

Você sempre pode adicionar uma linha artificial à sua tabela Customer, algo como Id = -1 e CustomerName = 'Unknown' e, em casos em que você normalmente definiria seu CustomerId em Order NULL, defina-o como -1.

Isso permite que você não tenha FKs anuláveis, mas ainda represente a falta de dados de forma apropriada (e o salvará de usuários downstream que não sabem como lidar com NULLs).

Stephen S.
fonte
Apenas para adicionar a isso, lembre-se de que NULLS não são armazenados em um índice (no oracle), então isso significa que pular a tabela de links e ir para o FK anulável faria sentido - em termos de desempenho. A outra coisa que pode depender é se você deseja armazenar mais alguma coisa nesta tabela de links, por exemplo, QUEM fez o link e quando? O link está inativo / excluído (mas já foi?)
Worthy7,
Esta é uma má ideia. Se você tiver uma chave estrangeira definida e os dados para os quais ela aponta forem removidos posteriormente, você não obterá a exceção de chave estrangeira e agora seus dados não têm sentido. Pior, se algo mais for atribuído a essa chave, você está apontando para o cliente totalmente errado
IcedDante
0

FKs anuláveis ​​para relações muitos para um opcionais são totalmente aceitáveis.

Henning
fonte
-1

Ouvi dizer que as colunas anuláveis ​​em geral quebram o primeiro grau de normalização. Mas na prática é muito prático.

Bryan McLemore
fonte
3
As colunas anuláveis ​​podem estar em 1NF a 5NF, mas não em 6NF de acordo com o que li.
Walter Mitty
-1

Sim, há algo errado. Não é uma chave estrangeira se for anulável. Seu banco de dados projetado por código. Talvez você faça um link zero para os não atribuídos. ou "Não atribuído" se estiver usando uma coluna de caractere. Mantenha a integridade de seus dados 100%.

danny117
fonte