Tecnicamente, NULL = NULL é False, por essa lógica, NULL é igual a qualquer NULL e todos os NULLs são distintos. Isso não significa que todos os NULLs são únicos e um índice exclusivo deve permitir qualquer número de NULLs?
Tecnicamente, NULL = NULL é False, por essa lógica, NULL é igual a qualquer NULL e todos os NULLs são distintos. Isso não significa que todos os NULLs são únicos e um índice exclusivo deve permitir qualquer número de NULLs?
Respostas:
Por que funciona dessa maneira? Desde quando alguém tomou uma decisão de design sem saber ou se importar com o que o padrão diz (afinal, temos todos os tipos de comportamentos estranhos com NULL
s e podemos coagir comportamentos diferentes à vontade). Essa decisão determinou que, neste caso NULL = NULL
,.
Não foi uma decisão muito inteligente. O que eles deveriam ter feito é fazer com que o comportamento padrão adira ao padrão ANSI e, se eles realmente desejam esse comportamento peculiar, permitam-no através de uma opção DDL como WITH CONSIDER_NULLS_EQUAL
ou WITH ALLOW_ONLY_ONE_NULL
.
Obviamente, retrospectiva é 20/20.
E agora temos uma solução alternativa, mesmo que não seja a mais limpa ou a mais intuitiva.
Você pode obter o comportamento ANSI adequado no SQL Server 2008 e acima, criando um índice filtrado exclusivo.
CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;
Isso permite mais de um NULL
valor, porque essas linhas são completamente deixadas de fora da verificação duplicada. Como um bônus adicional, isso acabaria sendo um índice menor do que aquele que consistia em toda a tabela se vários NULL
s fossem permitidos (especialmente quando não é a única coluna no índice, possui INCLUDE
colunas, etc.). No entanto, convém conhecer algumas das outras limitações dos índices filtrados:
Corrigir. A implementação de uma restrição ou índice exclusivo no servidor sql permite um e apenas um NULL. Também corrija que isso tecnicamente não se encaixa na definição de NULL, mas é uma daquelas coisas que eles fizeram para torná-lo mais útil, mesmo que não seja "tecnicamente" correto. Observe que uma PRIMARY KEY (também um índice exclusivo) não permite NULLs (é claro).
Primeiro - pare de usar a frase "Valor nulo", isso apenas o desviará. Em vez disso, use a frase "marcador nulo" - um marcador em uma coluna indicando que o valor real nessa coluna está ausente ou não é aplicável (mas observe que o marcador não diz qual dessas opções é realmente o caso¹).
Agora, imagine o seguinte (onde o banco de dados não possui conhecimento completo da situação modelada).
Situation Database
ID Code ID Code
-- ----- -- -----
1 A 1 A
2 B 2 (null)
3 C 3 C
4 B 4 (null)
A regra de integridade que estamos modelando é "o código deve ser único". A situação do mundo real viola isso; portanto, o banco de dados não deve permitir que os itens 2 e 4 estejam na tabela ao mesmo tempo.
A abordagem mais segura e menos flexível seria desabilitar marcadores nulos no campo Código, para que não haja possibilidade de dados inconsistentes. A abordagem mais flexível seria permitir vários marcadores nulos e se preocupar com a exclusividade quando os valores são inseridos.
Os programadores da Sybase adotaram a abordagem um tanto segura e pouco flexível de permitir apenas um marcador nulo na tabela - algo que os comentaristas reclamam desde então. A Microsoft continuou esse comportamento, acho que para compatibilidade com versões anteriores.
¹ Tenho certeza de que li em algum lugar que o Codd considerou implementar dois marcadores nulos - um para desconhecido e outro para inaplicável - mas o rejeitou, mas não consigo encontrar a referência. Estou me lembrando corretamente?
PS Minha citação favorita sobre null: Louis Davidson, "Design de banco de dados profissional do SQL Server 2000", Wrox Press, 2001, página 52. "Resumiu em uma única frase: NULL é mau".
null
também não atinja esse objetivo. Como o valor ausente pode ser o mesmo que o valor em uma das outras linhas.
CHECK (Value IN ('A','B','C','D'))
? Em seguida, a implementação do SQL Server e o padrão SQL permitem que a tabela tenha 5 linhas (uma linha para cada valor mais 1 com NULL.) Então, sem dúvida, embora o banco de dados seja consistente com suas restrições, não é consistente com a intenção do designer de a tabela deve ter no máximo 4 linhas. Não há valor que o NULL possa ser alterado para não violar uma restrição, a menos que uma ou mais linhas sejam excluídas.
CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;
irá gerar um erro. De acordo com sua teoria das motivações do projeto, deveria ter impedido a inserção NULL
no primeiro caso - porque o conhecimento incompleto significa que não há garantia de que o valor seja diferente.
Isso pode não ser tecnicamente preciso, mas filosoficamente me ajuda a dormir à noite ...
Como vários outros já disseram ou fizeram alusão, se você pensa em NULL como desconhecido, não pode determinar se um valor NULL é de fato igual a outro valor NULL. Pensando dessa maneira, a expressão NULL == NULL deve ser avaliada como NULL, ou seja, desconhecida.
Uma restrição exclusiva precisaria de um valor definitivo para a comparação dos valores da coluna. Em outras palavras, ao comparar um único valor da coluna com qualquer outro valor da coluna usando o operador de igualdade, ele deve avaliar como false para ser válido. O desconhecido não é realmente falso, embora seja frequentemente tratado como falso. Dois valores NULL podem ser iguais ou não ... simplesmente não podem ser determinados definitivamente.
Ajuda pensar em uma restrição única como valores restritivos que podem ser determinados como distintos um do outro. O que quero dizer com isso é se você executar um SELECT que se parece com isso:
SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"
A maioria das pessoas esperaria um resultado, uma vez que existe uma restrição única. Se você permitisse vários valores NULL em ColumnWithUniqueConstraint, seria impossível selecionar uma única linha distinta da tabela usando NULL como o valor comparado.
Dado isso, acredito que, independentemente de ser implementado com precisão ou não com relação à definição de NULL, é definitivamente muito mais prático na maioria das situações do que permitir vários valores NULL.
Um dos principais objetivos de uma UNIQUE
restrição é impedir registros duplicados. Se for necessário ter uma tabela na qual possa haver vários registros em que um valor seja "desconhecido", mas não for permitido que dois registros tenham o mesmo valor "conhecido", os valores desconhecidos deverão receber identificadores artificiais exclusivos antes de serem adicionado à tabela.
Existem alguns casos raros em que uma coluna que possui uma UNIQUE
restrição e contém um único valor nulo; por exemplo, se uma tabela contiver um mapeamento entre os valores da coluna e as descrições de texto localizadas, uma linha para NULL
permitiria definir a descrição que deve aparecer quando essa coluna em alguma outra tabela estiver NULL
. O comportamento de NULL
permite esse caso de uso.
Caso contrário, não vejo base para um banco de dados com UNIQUE
restrição em nenhuma coluna para permitir a existência de muitos registros idênticos, mas não vejo como impedir isso ao permitir vários registros cujos valores-chave não são distinguíveis. Declarar que NULL
não é igual a si mesmo não tornará os NULL
valores distinguíveis um do outro.