¿Cuáles son las consecuencias de no especificar NOT NULL en PostgreSQL para campos que no pueden ser nulos?

10

Tengo una aplicación (los datos se almacenan en PostgreSQL), donde la mayoría de los campos en las tablas no siempre son nulos, pero el esquema para estas tablas no impone esto. Por ejemplo, mira esta tabla falsa:

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

También name, num, timeNo se indican explícitamente como NOT NULL, en realidad son, debido a la aplicación ocurre en el lado de la aplicación.


Mi sensación es que debería cambiarse, pero el contrapunto es que el nivel de aplicación se asegura de que los valores nulos no puedan aparecer aquí y nadie más modifique manualmente la tabla.

Mi pregunta es : ¿Cuáles son los beneficios (rendimiento, almacenamiento, consistencia, algo más) e inconvenientes (suponiendo que ya verifiqué que no hay nulos presentes en este momento, y de la lógica de negocios no debería haber nulos) estableciendo un NOT NULLrestricción explícita ?

Tenemos un buen proceso de revisión de código y una documentación razonablemente buena, por lo que la posibilidad de que alguna persona nueva cometa algo que rompa esta restricción no es realmente suficiente para justificar el cambio.

Esta no es mi decisión, así que es exactamente por eso que estoy buscando otras justificaciones. En mi opinión, si algo no puede ser nulo y una base de datos le permite especificar que algo no es nulo, entonces simplemente hágalo. Especialmente si el cambio es super simple.

Salvador Dalí
fuente
1
Vea esta respuesta para Nulos y consideraciones de espacio en disco: stackoverflow.com/questions/5008753/... En resumen, si su tabla tiene más de 8 columnas y al menos 1 columna anulable, la tabla necesitará más bytes por fila que si todas las columnas son definido no nulo.
ypercubeᵀᴹ
1
@ ypercubeᵀᴹ: Para ser precisos, el mapa de bits nulo solo se agrega por fila si hay un valor nulo real en la fila: stackoverflow.com/a/7654497/939860 . Por lo tanto, las NOT NULLrestricciones no tienen ningún efecto directo sobre el tamaño de almacenamiento. Por supuesto, con todas las columnas definidas NOT NULL, no puede haber un mapa de bits nulo para empezar. Por otro lado: el tamaño de almacenamiento suele ser mucho más pequeño si usa NULL en lugar de valores "vacíos" o ficticios para columnas sin valor real, porque el mapa de bits nulo es comparativamente mucho más pequeño (excepto en casos extremos poco comunes).
Erwin Brandstetter
@ErwinBrandstetter mi mal entonces, no había entendido esa parte. Entonces, para las columnas que no tienen valores nulos, no hay una diferencia real en el almacenamiento, ya sea que las defina como NULL o NOT NULL, ¿correcto? ¿Es lo mismo para el espacio de almacenamiento de índice también?
ypercubeᵀᴹ
55
"el nivel de aplicación se asegura de que los valores nulos no puedan aparecer aquí" No, no lo hace. Se podría asegurarse de que una aplicación no hace inserto nulos. Pero tengo psql (por ejemplo), y puedo insertar nulos tanto deliberada como accidentalmente sin que su aplicación lo sepa.
Mike Sherrill 'Cat Recall'
44
La única aplicación que puede asegurarse de que nadie modifique manualmente la tabla es el propio dbms.
Mike Sherrill 'Cat Recall'

Respuestas:

9

¿Qué sucede cuando llega un nuevo programador y tiene que escribir una aplicación contra esa base de datos? No saben que ese campo x tiene que ser NOT NULL.

Otro programa podría suponer que todos los campos x son NOT NULLpara realizar recuentos, pero algunos ahora se deben NULLal nuevo programa, lo que lleva a errores inconsistentes y difíciles de rastrear.

En mi humilde opinión, siempre es mejor hacer cumplir las reglas de integridad de datos lo más cerca posible de los datos, es decir, en la base de datos. De esa manera, las nuevas aplicaciones y / o programadores no pueden arruinar sus datos.

Programadores, aplicaciones, lenguajes y frameworks van y vienen. Los datos y las bases de datos tienden a persistir. La base de datos es su última línea de defensa contra datos inconsistentes y potencialmente erróneos.

Aproveche al máximo los mecanismos de aplicación de restricciones de integridad de su base de datos, incluso a expensas del rendimiento. ¡Un sistema lento que produce resultados correctos es infinitamente superior a uno rápido que hace las cosas mal!

Vérace
fuente
1
IMHO it is always best to enforce data integrity rules as near to the data as possibleesto es realmente lo mismo que el presentimiento sobre el que escribí. Y esto es exactamente por qué estoy buscando justificaciones reales. Tenemos una revisión de código y una buena documentación, por lo que las preocupaciones sobre un nuevo desarrollador que no sabe algo no son suficientes para justificar el cambio.
Salvador Dali
44
Las revisiones de código y la buena documentación no le garantizan contra errores (de programación u otros).
ypercubeᵀᴹ
2
¿Y cuántos REAL PROGRAMMERSleen toda la documentación (o incluso alguna) antes de atascarse en un proyecto en el que tienen un plazo ajustado?
Vérace
3
Una vez hice una revisión en un banco que tenía la misma actitud para su almacén de datos. En su caso, no hay integridad referencial. Bueno, sucede que el 40% de los datos anteriores eran basura porque alguien no había leído la documentación y borró los datos en las tablas de búsqueda. No confía en las revisiones de código y la documentación con integridad de datos; lo hace explícito en la base de datos.
TomTom
5

Como ya mencionaron otros en los comentarios, agregar NOT NULLa la especificación de su tabla puede mejorar en un manera significativa el rendimiento de sus consultas (además de las muy buenas razones metodológicas establecidas en otra respuesta).

La razón es que el optimizador de consultas, sabiendo que una columna no puede tener un NULLvalor, puede excluir pruebas especiales para tales valores, como en el caso NOT INvs. NOT EXISTSPuede ver, por ejemplo, este blog , donde se muestra que no declarar un campo NOT NULL(cuando la tabla contiene siempre valores no nulos) con una consulta determinada aumenta el tiempo de ejecución en un 500%. El resultado se muestra para SQL Server, pero un comportamiento similar podría estar presente en otros DBMS relacionales, como el suyo (sin mencionar el hecho de que su base de datos podría ser portada a otros sistemas). Una regla general que puede asumir es que cuando hay más información disponible para el optimizador de consultas, se pueden generar planes de acceso más eficientes.

Renzo
fuente
Gracias. Este es el tipo de respuesta que estaba buscando.
Salvador Dali
55
Las columnas que nunca contienen NULL, deben definirse NOT NULLpor múltiples razones, no hay argumento al respecto. Pero el enlace al blog sobre SQL Server no es aplicable para Postgres y no prueba ninguna de las implicaciones de rendimiento que usted menciona. No digo que no haya ninguno, pero me encantaría ver evidencia real .
Erwin Brandstetter
@ErwinBrandstetter, tuve muchas expectativas sobre el optimizador PostgreSQL :( Después de varias pruebas no encontré diferencias significativas en la consulta NOT IN presentada en el blog en PostgreSQL con y sin una restricción NOT NULL. Entonces, cambié la respuesta y te pido si usted piensa que yo debería eliminar por completo.
Renzo
No, no creo que deba eliminarse. Tiene más de 5 votos y no tiene voto negativo, por ejemplo.
ypercubeᵀᴹ
Sin not inembargo, la semántica de las columnas anulables es diferente, por lo que debe haber alguna diferencia en el plan entre las dos.
Martin Smith
2

Implicaciones espaciales

Las implicaciones espaciales se habla en este post por @Erwin Brandstetter

En resumen, guardará un totalColumns - 8bit redondeado al byte más cercano (o MAXALIGN), si su base de datos tiene

  1. Más de 8 columnas
  2. TODAS las columnas de la tabla sonNOT NULL

Implicaciones de rendimiento

Sin embargo, en esta publicación en SE de @Erwin Brandstetter , dice

  1. "La configuración NOT NULL no tiene efecto per se en el rendimiento. Unos pocos ciclos para la comprobación, irrelevante".
  2. "... al utilizar NULL en lugar de valores ficticios. Dependiendo de los tipos de datos, puede ahorrar mucho espacio en disco y RAM, acelerando así ... todo".

@Renzo tiene una respuesta que habla sobre las implicaciones de rendimiento: supongo que nada de eso es aplicable a PostgreSQL . No puedo encontrar nada que corrobore nada de eso como relevante para PostgreSQL. Los ciclos que se guardan no se pueden cuantificar ni siquiera en la consulta más rudimentaria.

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;

Además, realicé algunas pruebas para ver si los índices NULL eran cada vez más rápidos, y no pude comprobarlo. Puede encontrar este hilo increíblemente útil de Scott Marlowe en las listas de correo que habla sobre el planificador de consultas en 9.1 que puede usar el índice parcial en cláusulas WHERE diferentes. Probé esto ejecutando lo siguiente

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
;

Ahora creé los índices,

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

En ambos casos, el planificador pudo usar el índice cuando seleccionó = 10y usó una exploración secuencial al buscar NULL o 0 respectivamente. Ambos índices parciales eran del mismo tamaño. Y, los índices completos (no mostrados) eran del mismo tamaño. Siguiendo la misma metodología, cargué la tabla con una secuencia de 1..1e5, y un valor nulo / 0, y otra secuencia de 1..1e5. Ambos métodos pudieron encontrar el nulo / 0 con un índice que cubre toda la tabla.

TLDR; Resumen

No puedo corroborar nada de una forma u otra en la mayoría de las preocupaciones de rendimiento que pensé que valía la pena probar para incluir las deficiencias del planificador. El beneficio de usar null para salvar ram es real. El espacio en disco ahorrado al no usar nulo es insignificante, y eso es una exageración en las tablas con una NULLABLEcolumna, o menos de 8 columnas. En esos casos no hay espacio en disco guardado.

Evan Carroll
fuente