Lembro-me de ler este artigo sobre design de banco de dados e também me lembro de dizer que você deve ter propriedades de campo NOT NULL. Eu não me lembro porque este foi o caso embora.
Tudo o que consigo pensar é que, como desenvolvedor de aplicativos, você não precisaria testar NULL e um possível valor de dados inexistente (por exemplo, uma string vazia para strings).
Mas o que você faz no caso de datas, datetime e time (SQL Server 2008)? Você teria que usar alguma data histórica ou de fundo.
Alguma idéia sobre isso?
Acho que a pergunta está mal formulada, pois o texto implica que você já decidiu que NULLs são ruins. Talvez você quis dizer "Devemos permitir NULLs?"
De qualquer forma, aqui está minha opinião: acho que NULLs são uma coisa boa. Quando você começa a evitar NULLs apenas porque "NULLs são ruins" ou "NULLs são difíceis", você começa a criar dados. Por exemplo, e se você não souber minha data de nascimento? O que você vai colocar na coluna até saber? Se você for como um monte de gente anti-NULL, você vai digitar 1900-01-01. Agora vou ser colocado na ala geriátrica e provavelmente receberei uma ligação da minha estação de notícias local parabenizando-me por minha longa vida, perguntando-me meus segredos para viver uma vida tão longa etc.
Se uma linha pode ser inserida onde é possível que você não saiba o valor de uma coluna, acho que NULL faz muito mais sentido do que escolher algum valor de token arbitrário para representar o fato de que é desconhecido - um valor que outros irão precisa saber, fazer engenharia reversa ou perguntar por aí para descobrir o que isso significa.
No entanto, há um equilíbrio - nem todas as colunas em seu modelo de dados devem ser anuláveis. Geralmente, há campos opcionais em um formulário ou informações que, de outra forma, não são coletadas no momento em que a linha é criada. Mas isso não significa que você pode adiar o preenchimento de todos os dados. :-)
Além disso, a capacidade de usar NULL pode ser limitada por requisitos cruciais na vida real. Na área médica, por exemplo, pode ser uma questão de vida ou morte saber por que um valor é desconhecido. A frequência cardíaca é NULA porque não havia pulso ou porque ainda não a medimos? Nesse caso, podemos colocar NULL na coluna de frequência cardíaca e ter notas ou uma coluna diferente com um motivo NULL-becau?
Não tenha medo de NULLs, mas esteja disposto a aprender ou ditar quando e onde eles devem ser usados, e quando e onde não devem.
As razões estabelecidas são:
NULL não é um valor e, portanto, não possui tipo de dados intrínseco. Nulos precisam de tratamento especial em todos os lugares quando o código que depende de tipos reais também pode receber o NULL não tipado.
NULL quebra a lógica de dois valores (familiar True ou False) e requer uma lógica de três valores. Isso é muito mais complexo até mesmo para implementar corretamente e certamente é mal compreendido pela maioria dos DBAs e quase todos os não-DBAs. Como consequência, ele convida positivamente muitos bugs sutis no aplicativo.
O significado semântico de qualquer NULL específico é deixado para o aplicativo , ao contrário dos valores reais.
Semânticas como “não aplicável” e “desconhecido” e “sentinela” são comuns, e existem outras também. Eles são frequentemente usados simultaneamente dentro de um mesmo banco de dados, mesmo dentro da mesma relação; e são, obviamente, significados inexplícitos e indistinguíveis e incompatíveis .
Eles não são necessários para bancos de dados relacionais , conforme argumentado em “Como lidar com informações ausentes sem nulos” . A normalização adicional é um primeiro passo óbvio para tentar livrar uma tabela de NULLs.
Isso não significa que NULL nunca deve ser permitido. Ele argumenta que há muitas boas razões para não permitir NULL sempre que possível.
Significativamente, ele argumenta para tentar muito – por meio de um melhor design de esquema, melhores mecanismos de banco de dados e linguagens de banco de dados ainda melhores – para tornar viável evitar NULL com mais frequência.
Fabian Pascal responde a uma série de argumentos, em “Nulls Nullified” .
Eu discordo, nulos são um elemento essencial do design de banco de dados. A alternativa, como você aludiu também, seria uma proliferação de valores conhecidos para representar o que falta ou o desconhecido. O problema está no fato de null ser tão amplamente incompreendido e, como resultado, ser usado de forma inadequada.
IIRC, Codd sugeriu que a implementação atual de null (significando não presente/ausente) poderia ser melhorada com dois marcadores nulos em vez de um, "não presente, mas aplicável" e "não presente e não aplicável". Não consigo imaginar como os designs relacionais seriam melhorados por isso pessoalmente.
Deixe-me começar dizendo que não sou um DBA, sou um desenvolvedor de coração e mantenho e atualizo nossos bancos de dados com base em nossas necessidades. Dito isto, eu tinha a mesma pergunta por alguns motivos.
Passo muito tempo vasculhando as cargas de respostas, comentários, artigos e conselhos por toda a internet. Escusado será dizer que a maioria das informações era quase a mesma da resposta de @AaronBertrand. Por isso senti a necessidade de responder a esta pergunta.
Em primeiro lugar, quero definir algo direto para todos os futuros leitores... Valores NULL representam dados desconhecidos NÃO dados não utilizados. Se você tiver uma tabela de funcionários que tenha um campo de data de rescisão. Um valor nulo na data de término é porque é um campo obrigatório futuro que é atualmente desconhecido. Todo funcionário, seja ele ativo ou demitido, em algum momento terá uma data adicionada a esse campo. Essa é, na minha opinião, a única razão para um campo anulável.
Dito isto, a mesma tabela de funcionários provavelmente conteria algum tipo de dados de autenticação. É comum em um ambiente corporativo que os funcionários sejam listados no banco de dados para RH e contabilidade, mas nem sempre tenham ou precisem de detalhes de autenticação. A maioria das respostas levaria você a acreditar que não há problema em anular esses campos ou, em alguns casos, criar uma conta para eles, mas nunca enviar as credenciais. O primeiro fará com que sua equipe de desenvolvimento escreva código para verificar se há NULLs e lidar com eles de acordo e o último representa um enorme risco de segurança! Contas que ainda não são usadas no sistema apenas aumentam o número de pontos de acesso possíveis para um hacker, além de ocuparem um valioso espaço de banco de dados para algo que nunca é usado.
Dadas as informações acima, a melhor maneira de lidar com dados anuláveis que serão usados é permitir valores anuláveis. É triste, mas é verdade e seus desenvolvedores vão odiá-lo por isso. O segundo tipo de dados anuláveis deve ser colocado em uma tabela relacionada (IE: Conta, Credenciais, etc) e ter um relacionamento Um para Um. Isso permite que um usuário exista sem credenciais, a menos que sejam necessárias. Isso elimina o risco extra de segurança, o valioso espaço do banco de dados e fornece um banco de dados muito mais limpo.
Abaixo está uma estrutura de tabela muito simplista que mostra a coluna anulável necessária e um relacionamento um para um.
Eu sei que estou um pouco atrasado para a festa desde que essa pergunta foi feita anos atrás, mas espero que isso ajude a lançar alguma luz sobre esse problema e a melhor forma de lidar com isso.
Além de todos os problemas com desenvolvedores NULL confusos, NULLs têm outra desvantagem muito séria: Desempenho
Colunas NULL'able são um desastre do ponto de vista de desempenho. Considere a aritmética de inteiros como um exemplo. Em um mundo são sem NULL, é "fácil" vetorizar aritmética inteira no código do mecanismo de banco de dados usando instruções SIMD para realizar praticamente qualquer cálculo em velocidades mais rápidas que 1 linha por ciclo de CPU. No entanto, no momento em que você introduz NULL, você precisa lidar com todos os casos especiais que NULL cria. Conjuntos de instruções de CPU modernos (leia-se: x86/x64/ARM e lógica de GPU também) simplesmente não estão equipados para fazer isso com eficiência.
Considere a divisão como um exemplo. Em um nível muito alto, esta é a lógica que você precisa com um inteiro não nulo:
Com NULL, isso se torna um pouco mais complicado. Junto com
b
você precisará de um indicador seb
for nulo e da mesma forma paraa
. O cheque agora se torna:A aritmética NULL é significativamente mais lenta para ser executada em uma CPU moderna do que a aritmética não nula (por um fator de cerca de 2-3x).
Fica pior quando você introduz o SIMD. Com SIMD, uma CPU Intel moderna pode realizar 4 divisões inteiras de 32 bits em uma única instrução, assim:
Agora, existem maneiras de lidar com NULL na terra SIMD também, mas isso requer o uso de mais vetores e registradores de CPU e fazer algumas máscaras de bits inteligentes. Mesmo com bons truques, a penalidade de desempenho da aritmética inteira NULL se arrasta para o intervalo de 5 a 10 vezes mais lento para expressões relativamente simples.
Algo como o acima vale para agregados e, até certo ponto, para junções também.
Em outras palavras: a existência de NULL no SQL é uma incompatibilidade de impedância entre a teoria do banco de dados e o projeto real dos computadores modernos. Há uma boa razão para NULL confundir os desenvolvedores - porque um inteiro não pode ser NULL na maioria das linguagens de programação sãs - não é assim que os computadores funcionam.
O artigo da Wikipedia sobre SQL Null tem algumas observações interessantes sobre o valor NULL e, como uma resposta independente de banco de dados, desde que você esteja ciente dos efeitos potenciais de ter valores NULL para seu RDBMS específico, eles são aceitáveis em seu design. Se não fossem, você não seria capaz de especificar colunas como anuláveis.
Apenas esteja ciente de como seu RDBMS lida com eles em operações SELECT, como matemática, e também em índices.
Perguntas interessantes.
É mais complicado do que isso. Null tem vários significados distintos e uma razão realmente importante para não permitir nulos em muitas colunas é que quando a coluna é nula isso significa uma e apenas uma coisa (ou seja, que não apareceu em uma junção externa). Além disso, permite definir padrões mínimos de entrada de dados, o que é realmente útil.
Isso ilustra um problema com nulos imediatamente, ou seja, que um valor armazenado em uma tabela pode significar "este valor não se aplica" ou "não sabemos". Com strings, uma string vazia pode servir como "isto não se aplica", mas com datas e horas, não existe tal convenção porque não há valor válido que convencionalmente signifique isso. Normalmente, você ficará preso usando NULLs.
Existem maneiras de contornar isso (adicionando mais relações e unindo), mas essas apresentam exatamente os mesmos problemas de clareza semântica que ter NULLs no banco de dados. Para esses bancos de dados eu não me preocuparia com isso. Não há nada que você possa fazer sobre isso realmente.
EDIT: Uma área onde NULLs são indispensáveis é em chaves estrangeiras. Aqui eles normalmente têm apenas um significado, idêntico ao nulo no significado de junção externa. Esta é uma exceção ao problema, é claro.
Uau, a resposta correta "Não permita NULLs quando você não precisa porque eles degradam o desempenho" é de alguma forma a última resposta avaliada. Vou upvote-lo e elaborar. Quando um RDBMS permite NULLs para uma coluna não esparsa, essa coluna é adicionada a um bitmap que rastreia se o valor é NULL para cada linha individual. Portanto, ao adicionar a capacidade NULL a uma coluna em uma tabela em que todas as colunas não permitem NULLs, você está aumentando o espaço de armazenamento necessário para salvar a tabela. Além disso, você está exigindo que o RDBMS leia e grave no bitmap, degradando o desempenho em todas as operações.
Além disso, em vários casos, permitir NULLs quebrará o 3NF. Embora eu não seja um defensor da 3NF como muitos dos meus colegas, considere o seguinte cenário:
Na tabela Person há uma coluna, chamada DateOfDeath, que é anulável. Se uma pessoa morreu, ele será preenchido com sua Data de Morte, caso contrário, será deixado NULL. Há também uma coluna de bits não anulável chamada IsAlive. Esta coluna é definida como 1 se a pessoa estiver viva e 0 se a pessoa estiver morta. A grande maioria dos procedimentos armazenados usa a coluna IsAlive, eles se importam apenas se uma pessoa está viva, não seu DateOfDeath.
No entanto, a coluna IsAlive interrompe a normalização do banco de dados, porque é completamente derivável de DateOfDeath. Mas como o IsAlive está conectado à maioria dos SPs, a solução direta é tornar DateOfDeath não anulável e atribuir um valor padrão à coluna caso a pessoa ainda esteja viva. Os poucos SPs que usam DateOfDeath podem ser reescritos para verificar a coluna IsAlive e somente honrar a DateOfDeath se a pessoa não estiver viva. Novamente, como a maioria dos SPs se preocupa apenas com IsAlive (um pouco) e não com DateOfDeath (uma data), usar esse padrão acelera o acesso consideravelmente.
Um script T-SQL útil para localizar colunas anuláveis sem NULLs em todos os esquemas é:
Se você executar isso em uma cópia do seu banco de dados de produção, poderá encontrar as colunas que os desenvolvedores marcaram como permitindo NULLs que não possuem NULLs na prática. A grande maioria deles pode ser marcada como NOT NULL, aumentando assim o desempenho e reduzindo o espaço de armazenamento.
Pode não ser possível eliminar todos os NULLs em todas as tabelas e ainda ter um design limpo, mas há uma vantagem considerável em eliminar o maior número possível de NULLs. O otimizador trabalha muito mais rápido com essas informações e, se você puder eliminar todos os NULLs em uma tabela, poderá recuperar uma quantidade considerável de espaço de armazenamento.
Eu sei que desempenho não é algo que os DBAs pensam muito, mas você só pode usar uma quantidade limitada de memória e poder de processador em uma solução, em algum momento você terá que começar a pensar em design lógico e físico .
Observe também que isso é apenas para RDBMSes verdadeiros e estou baseando a parte técnica de minhas respostas no SQL Server. O T-SQL listado para localizar colunas anuláveis sem nulos também é do SQL Server.