O ORACLE não permite valores NULL em nenhuma das colunas que compreendem uma chave primária. Parece que o mesmo se aplica à maioria dos outros sistemas "corporativos".
Ao mesmo tempo, a maioria dos sistemas também permite restrições exclusivas em colunas anuláveis.
Por que as restrições exclusivas podem ter NULLs, mas as chaves primárias não podem? Existe uma razão lógica fundamental para isso, ou isso é mais uma limitação técnica?
database
database-design
Roman Starkov
fonte
fonte
Respostas:
As chaves primárias destinam-se à identificação exclusiva de linhas. Isso é feito comparando todas as partes de uma chave com a entrada.
Por definição, NULL não pode fazer parte de uma comparação bem-sucedida. Até uma comparação
NULL = NULL
consigo mesma ( ) falhará. Isso significa que uma chave contendo NULL não funcionaria.Additonally, NULL é permitido em uma chave estrangeira, para marcar um relacionamento opcional. (*) Permitir no PK também quebraria isso.
(*) Uma palavra de cautela: Ter chaves estrangeiras anuláveis não é um design de banco de dados relacional limpo.
Se houver duas entidades
A
eB
ondeA
opcionalmente pode ser relacionadoB
, a solução limpa é criar uma tabela de resolução (digamosAB
). Que a tabela ligariaA
comB
: Se não é um relacionamento, então ele iria conter um registro, se houver não é , então ele não iria.fonte
Uma chave primária define um identificador exclusivo para cada linha de uma tabela: quando uma tabela possui uma chave primária, você tem uma maneira garantida de selecionar qualquer linha nela.
Uma restrição exclusiva não identifica necessariamente todas as linhas; ele só especifica que se uma linha tem valores em suas colunas, em seguida, eles devem ser exclusivos. Isso não é suficiente para identificar exclusivamente cada linha, que é o que uma chave primária deve fazer.
fonte
Fundamentalmente, nada está errado com um NULL em uma chave primária de várias colunas. Mas ter uma delas tem implicações que o designer provavelmente não pretendia, e é por isso que muitos sistemas lançam um erro ao tentar fazer isso.
Considere o caso das versões de módulo / pacote armazenadas como uma série de campos:
Os 5 primeiros elementos da chave primária são partes regularmente definidas de uma versão de lançamento, mas alguns pacotes têm uma extensão personalizada que geralmente não é um número inteiro (como "rc-foo" ou "vanilla" ou "beta" ou qualquer outra pessoa para quem quem quatro campos é insuficiente pode sonhar). Se um pacote não tiver uma extensão, será NULL no modelo acima, e nenhum dano seria causado ao deixar as coisas dessa maneira.
Mas o que é um NULL? Supõe-se que representa uma falta de informação, um desconhecido. Dito isto, talvez isso faça mais sentido:
Nesta versão, a parte "ext" da tupla NÃO é NULL, mas o padrão é uma string vazia - que é semanticamente (e praticamente) diferente de um NULL. Um NULL é um desconhecido, enquanto uma sequência vazia é um registro deliberado de "algo que não está presente". Em outras palavras, "vazio" e "nulo" são coisas diferentes. É a diferença entre "não tenho valor aqui" e "não sei qual é o valor aqui".
Quando você registra um pacote que não possui uma extensão de versão, você sabe que ele não possui uma extensão; portanto, uma string vazia é realmente o valor correto. Um NULL só estaria correto se você não soubesse se tinha uma extensão ou não, ou você sabia que sim, mas não sabia o que era. É mais fácil lidar com essa situação em sistemas em que os valores de string são a norma, porque não há como representar um "número inteiro vazio" além da inserção de 0 ou 1, que acabará sendo acumulado em quaisquer comparações feitas posteriormente (que suas próprias implicações).
Aliás, as duas formas são válidas no Postgres (já que estamos discutindo RDMBSs "corporativos"), mas os resultados da comparação podem variar bastante quando você lança um NULL na mistura - porque NULL == "não sabe", então todos os resultados de uma comparação envolvendo um NULL acabam sendo NULL, pois você não pode saber algo que é desconhecido. PERIGO! Pense com cuidado: isso significa que os resultados da comparação NULL se propagam através de uma série de comparações. Isso pode ser uma fonte de erros sutis ao classificar, comparar etc.
O Postgres assume que você é adulto e pode tomar essa decisão por si mesmo. O Oracle e o DB2 assumem que você não percebeu que estava fazendo algo bobo e emitiu um erro. Esta é geralmente a coisa certa, mas nem sempre - você pode realmente não sei e ter um NULL em alguns casos e, portanto, deixando uma linha com um elemento desconhecido contra o qual comparações significativas são impossíveis é o comportamento correto.
Em qualquer caso, você deve se esforçar para eliminar o número de campos NULL permitidos em todo o esquema e duplamente quando se trata de campos que fazem parte de uma chave primária. Na grande maioria dos casos, a presença de colunas NULL é uma indicação de design de esquema não normalizado (em oposição a deliberadamente desnormalizado) e deve ser pensado muito antes de ser aceito.
[* NOTA: É possível criar um tipo personalizado que é a união de números inteiros e um tipo "inferior" que semanticamente significaria "vazio" em oposição a "desconhecido". Infelizmente, isso introduz um pouco de complexidade nas operações de comparação e, geralmente, ser verdadeiramente correto do tipo não vale o esforço na prática, pois você não deve permitir muitos
NULL
valores em primeiro lugar. Dito isto, seria maravilhoso se os RDBMSs incluíssem umBOTTOM
tipo padrão , alémNULL
de impedir o hábito de conflitar casualmente a semântica de "sem valor" com "valor desconhecido". ]fonte
NULL == NULL -> false (pelo menos nos DBMSs)
Portanto, você não seria capaz de recuperar nenhum relacionamento usando um valor NULL, mesmo com colunas adicionais com valores reais.
fonte
where pk_1 = 'a' and pk_2 = 'b'
com valores normais e alternar parawhere pk_1 is null and pk_2 = 'b'
quando houver nulos.where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))
/A resposta de Tony Andrews é decente. Mas a resposta real é que essa tem sido uma convenção usada pela comunidade de bancos de dados relacionais e NÃO é uma necessidade. Talvez seja uma boa convenção, talvez não.
Comparar qualquer coisa com NULL resulta em DESCONHECIDO (terceiro valor de verdade). Assim, como foi sugerido com nulos, toda a sabedoria tradicional sobre igualdade sai pela janela. Bem, é assim que parece à primeira vista.
Mas não acho que isso seja necessariamente verdade e nem os bancos de dados SQL acham que o NULL destrói todas as possibilidades de comparação.
Execute no seu banco de dados a consulta SELECT * FROM VALUES (NULL) UNION SELECT * FROM VALUES (NULL)
O que você vê é apenas uma tupla com um atributo que tem o valor NULL. Portanto, a união reconheceu aqui os dois valores NULL como iguais.
Ao comparar uma chave composta que possui 3 componentes a uma tupla com 3 atributos (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 AND 3 = 3 AND NULL = NULL O resultado disso é DESCONHECIDO .
Mas poderíamos definir um novo tipo de operador de comparação, por exemplo. ==. X == Y <=> X = Y OU (X É NULO E Y É NULO)
Ter esse tipo de operador de igualdade tornaria sem problemas as chaves compostas com componentes nulos ou chaves não compostas com valor nulo.
fonte
Eu ainda acredito que esta é uma falha fundamental / funcional provocada por um tecnicismo. Se você possui um campo opcional pelo qual pode identificar um cliente, agora precisa hackear um valor fictício nele, apenas porque NULL! = NULL, não é particularmente elegante, mas é um "padrão do setor"
fonte