Quais são as consequências de não especificar NOT NULL no PostgreSQL para campos que não podem ser nulos?

10

Eu tenho um aplicativo (os dados são armazenados no PostgreSQL), onde a maioria dos campos nas tabelas nem sempre é nula, mas o esquema para essas tabelas não impõe isso. Por exemplo, veja esta tabela falsa:

CREATE TABLE "tbl" (
    "id" serial,
    "name" varchar(40),
    "num" int,
    "time" timestamp
    PRIMARY KEY ("id"),
    UNIQUE ("id")
);

Além disso name, num, timenão são explicitamente indicado como NOT NULL, na realidade, eles são, porque a aplicação acontece no lado de aplicação.


Meu sentimento é que ele deve ser alterado, mas o contraponto é que o nível do aplicativo garante que os valores nulos não possam aparecer aqui e ninguém mais modifique manualmente a tabela.

Minha pergunta é : Quais são os benefícios (desempenho, armazenamento, consistência, outra coisa) e desvantagens (supondo que eu já verifiquei que não há nulos presentes no momento e, pela lógica de negócios, não deve haver nulos) definindo um NOT NULLrestrição explícita ?

Temos um bom processo de revisão de código e uma documentação razoavelmente boa; portanto, a possibilidade de alguém novo cometer algo que rompa essa restrição não é realmente suficiente para justificar a alteração.

Esta não é uma decisão minha, por isso é exatamente por que estou procurando outras justificativas. Na minha opinião, se algo não puder ser nulo e um banco de dados permitir que você especifique que algo não é nulo - basta fazê-lo. Especialmente se a mudança for super simples.

Salvador Dalí
fonte
11
Consulte esta resposta para obter considerações sobre Nulos e espaço em disco: stackoverflow.com/questions/5008753/… Resumindo, se sua tabela tiver mais de 8 colunas e pelo menos 1 coluna anulável, a tabela precisará de mais bytes por linha do que se todas as colunas forem definido não nulo.
precisa saber é o seguinte
11
@ ypercubeᵀᴹ: Para ser mais preciso, o bitmap nulo será adicionado apenas por linha se houver um valor nulo real na linha: stackoverflow.com/a/7654497/939860 . Portanto, as NOT NULLrestrições não têm efeito direto no tamanho do armazenamento. Obviamente, com todas as colunas sendo definidas NOT NULL, não pode haver um bitmap nulo para começar. Por outro lado: o tamanho do armazenamento é geralmente muito menor se você usar NULL em vez de valores "vazios" ou fictícios para colunas sem valor real, porque o bitmap nulo é comparativamente muito menor (exceto casos de borda raros).
Erwin Brandstetter
@ErwinBrandstetter meu mal então, não tinha entendido essa parte. Portanto, para colunas que não têm valores nulos, não há diferença real no armazenamento, se você as define como NULL ou NOT NULL, correto? Também é o mesmo para o espaço de armazenamento de índice?
precisa saber é o seguinte
5
"o nível do aplicativo garante que valores nulos não possam aparecer aqui" Não, não aparece. Ele pode ter certeza de que uma aplicação não insere valores nulos. Mas eu tenho o psql (por exemplo) e posso inserir nulos de forma deliberada e acidental sem que seu aplicativo saiba disso.
Mike Sherrill 'Cat Recall'
5
O único aplicativo que pode garantir que ninguém modifique manualmente a tabela é o próprio dbms.
Mike Sherrill 'Cat Recall'

Respostas:

9

O que acontece quando um novo programador chega e precisa escrever um aplicativo nesse banco de dados? Eles não sabem que o campo x deve ser NOT NULL.

Outro programa pode assumir que todos os campos x são NOT NULLpara executar contagens, digamos, mas alguns agora são NULLpor causa do novo programa, levando a erros inconsistentes e difíceis de rastrear.

IMHO é sempre melhor aplicar regras de integridade de dados o mais próximo possível dos dados, ou seja, no banco de dados. Dessa forma, novos aplicativos e / ou programadores não podem atrapalhar seus dados.

Programadores, aplicativos, linguagens e estruturas vêm e vão. Dados e bancos de dados tendem a persistir. O banco de dados é sua última linha de defesa contra dados inconsistentes e potencialmente errôneos.

Aproveite ao máximo os mecanismos de imposição de restrições de integridade do seu banco de dados, mesmo à custa do desempenho. Um sistema lento que produz resultados corretos é infinitamente superior a um sistema rápido que erra as coisas!

Vérace
fonte
11
IMHO it is always best to enforce data integrity rules as near to the data as possiblena verdade, é o mesmo que o instinto sobre o qual escrevi. E é exatamente por isso que estou procurando justificativas reais. Temos uma revisão de código em vigor e boa documentação, portanto, as preocupações com um novo desenvolvedor que não sabe algo não são suficientes para justificar a alteração.
Salvador Dali
4
Revisões de código e boa documentação não garantem contra erros (de programação ou outros).
precisa saber é o seguinte
2
E quantos REAL PROGRAMMERSleem toda (ou mesmo alguma) da documentação antes de ficarem presos em um projeto em que eles têm um prazo apertado?
Vérace 6/06/16
3
Certa vez, fiz uma revisão em um banco que tinha a mesma atitude em relação ao data warehouse. No caso deles - nenhuma integridade referencial. Bem, acontece que 40% dos dados mais antigos eram lixo porque alguém não tinha lido a documentação e excluído dados nas tabelas de pesquisa. Você não confia nas revisões de código e na documentação com integridade dos dados - é explicitado no banco de dados.
TomTom
5

Como já citado por outros nos comentários, adicionar NOT NULLà especificação da sua tabela pode melhorar de maneira significativa o desempenho de suas consultas (além das boas razões metodológicas declaradas em outra resposta).

O motivo é que o otimizador de consulta, sabendo que uma coluna não pode ter um NULLvalor, pode excluir testes especiais para esses valores, como no caso NOT INvs. NOT EXISTSVocê pode ver, por exemplo, este blog , onde é mostrado que não declarar um campo NOT NULL(quando a tabela sempre contém valores não nulos) com uma determinada consulta aumenta o tempo de execução de 500%. O resultado é mostrado para o SQL Server, mas um comportamento semelhante pode estar presente em outros DBMSs relacionais, como o seu (para não mencionar o fato de que seu banco de dados pode ser portado para outros sistemas). Uma regra geral que você pode assumir é que, quando mais informações estão disponíveis para o otimizador de consultas, planos de acesso mais eficientes podem ser produzidos.

Renzo
fonte
Obrigado. Esse é o tipo de resposta que eu estava procurando.
Salvador Dali
5
As colunas que nunca contêm NULL devem ser definidas NOT NULLpor várias razões, sem argumento sobre isso. Mas o link para o blog sobre o SQL Server não é aplicável ao Postgres e não prova nenhuma das implicações de desempenho mencionadas. Não estou dizendo que não há, mas eu adoraria ver evidências reais .
Erwin Brandstetter
@ErwinBrandstetter, eu tinha grandes expectativas sobre o otimizador do PostgreSQL :( Após vários testes, não encontrei diferenças significativas na consulta NOT IN apresentada no blog do PostgreSQL com e sem restrição NOT NULL. Então, alterei a resposta , e estou perguntando se você acha que eu deveria eliminá-lo completamente.
Renzo
Não, acho que não deve ser excluído. Tem mais de 5 votos e nenhum voto negativo, por um.
ypercubeᵀᴹ
A semântica de not incolunas anuláveis ​​é diferente, portanto, deve haver alguma diferença no plano entre as duas.
Martin Smith
2

Implicações espaciais

As implicações espaciais são discutidas neste post por @Erwin Brandstetter

Em resumo, você salvará um totalColumns - 8bit arredondado para o byte mais próximo (ou MAXALIGN), se o seu banco de dados tiver

  1. Mais de 8 colunas
  2. TODAS as colunas na tabela sãoNOT NULL

Implicações de desempenho

No entanto, neste post no SE de @Erwin Brandstetter , ele diz

  1. "A configuração de NOT NULL não tem efeito per se no desempenho. Alguns ciclos para a verificação - irrelevantes."
  2. "... usando realmente NULLs em vez de valores fictícios. Dependendo dos tipos de dados, você pode economizar muito espaço em disco e RAM, acelerando, assim, tudo."

O @Renzo tem uma resposta que fala sobre as implicações de desempenho - eu assumiria que nada disso é aplicável ao PostgreSQL . Eu não consigo encontrar nada que substancia nenhum de que como sendo relevantes para PostgreSQL. Quaisquer que sejam os ciclos salvos, não podem ser quantificados nem na consulta mais rudimentar.

CREATE TABLE foo (
  a int,
  b int NOT NULL,
  x float,
  y float NOT NULL
);

INSERT INTO foo ( a, b, x, y )
SELECT x, x, x, x
FROM generate_series(1,1E7) AS X(x);

EXPLAIN ANALYZE SELECT 1/a FROM foo;
EXPLAIN ANALYZE SELECT 1/b FROM foo;
EXPLAIN ANALYZE SELECT 1/x FROM foo;
EXPLAIN ANALYZE SELECT 1/y FROM foo;

Além disso, fiz alguns testes para verificar se os índices NULL eram cada vez mais rápidos e não consegui comprovar isso. Você pode encontrar esse encadeamento extremamente útil de Scott Marlowe nas listas de discussão que falam sobre o planejador de consultas na 9.1 ser capaz de usar índice parcial em cláusulas WHERE diferentes. Eu testei isso executando o seguinte

CREATE TABLE foo ( a int );
CREATE TABLE bar ( a int NOT NULL );
INSERT INTO foo
  SELECT null FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT null FROM generate_series(1,1e5) AS x
;
INSERT INTO bar
  SELECT 0 FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT 0 FROM generate_series(1,1e5) AS x
;

Agora eu criei os índices,

CREATE INDEX foobar ON foo(a) WHERE a IS NOT NULL;
CREATE INDEX barbar ON bar(a) WHERE a <> 0;

Nos dois casos, o planejador conseguiu usar o índice ao selecionar = 10e usar uma verificação seq ao procurar por NULL ou 0, respectivamente. Ambos os índices parciais tinham o mesmo tamanho. E, índices completos (não mostrados) tinham o mesmo tamanho. Seguindo a mesma metodologia, carreguei a tabela com uma sequência de 1..1e5, e o valor nulo / 0, e outra sequência de 1..1e5. Ambos os métodos foram capazes de encontrar o nulo / 0 com um índice cobrindo a tabela inteira.

TLDR; Sumário

Não posso substanciar nada de uma maneira ou de outra na maioria das preocupações de desempenho que achei que valia a pena testar por incluir inadequações do planejador. O benefício de usar null para salvar ram é real. O espaço em disco economizado por não usar nulo é insignificante, e isso é um exagero em tabelas com uma NULLABLEcoluna ou menos de 8 colunas. Nesses casos, não há espaço em disco salvo.

Evan Carroll
fonte