Quali sono le conseguenze della mancata specificazione di NOT NULL in PostgreSQL per i campi che non possono essere nulli?


10

Ho un'applicazione (i dati sono archiviati in PostgreSQL), dove la maggior parte dei campi nelle tabelle non è sempre nulla, ma lo schema per queste tabelle non lo applica. Ad esempio guarda questo falso tavolo:

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

Inoltre name, num, timenon sono esplicitamente dichiarato come NOT NULL, in realtà sono, perché l'esecuzione avviene sul lato di applicazione.


La mia sensazione è che dovrebbe essere cambiato, ma il contrappunto è che il livello dell'applicazione si assicura che i valori null non possano apparire qui e nessun altro modifica manualmente la tabella.

La mia domanda è : quali sono i vantaggi (prestazioni, archiviazione, coerenza, qualcos'altro) e gli svantaggi (supponendo che ho già verificato che al momento non siano presenti null e che dalla logica aziendale non dovrebbero esserci null) impostando un NOT NULLvincolo esplicito ?

Abbiamo un buon processo di revisione del codice e una documentazione ragionevolmente buona, quindi la possibilità che una persona nuova commetterebbe qualcosa che rompa questo vincolo non è davvero sufficiente per giustificare il cambiamento.

Questa non è una mia decisione, quindi è esattamente per questo che cerco altre giustificazioni. Secondo me, se qualcosa non può essere nullo e un database ti consente di specificare che qualcosa non è null, allora fallo. Soprattutto se il cambiamento è super semplice.


1
Vedere questa risposta per considerazioni su spazio nullo e spazio su disco: stackoverflow.com/questions/5008753/… In breve, se la tabella ha più di 8 colonne e almeno 1 colonna nullable, la tabella avrà bisogno di più byte per riga rispetto a se tutte le colonne sono definito non nullo.
ypercubeᵀᴹ

1
@ ypercubeᵀᴹ: Per essere precisi, la bitmap null viene aggiunta per riga solo se nella riga è presente un valore null effettivo: stackoverflow.com/a/7654497/939860 . Pertanto, i NOT NULLvincoli non hanno alcun effetto diretto sulla dimensione della memoria. Naturalmente, con tutte le colonne definite NOT NULL, non può esserci una bitmap nulla per cominciare. D'altra parte: la dimensione della memoria è in genere molto più piccola se si utilizzano valori NULL anziché "vuoti" o fittizi per le colonne senza valore reale, poiché la bitmap null è relativamente più piccola (tranne per i rari casi limite).
Erwin Brandstetter,

@ErwinBrandstetter il mio cattivo allora, non avevo capito quella parte. Quindi, per le colonne che non hanno valori nulli, non vi è alcuna reale differenza nella memoria, sia che tu li definisca NULL o NOT NULL, giusto? È lo stesso anche per lo spazio di archiviazione dell'indice?
ypercubeᵀᴹ

5
"il livello dell'applicazione si assicura che i valori null non possano apparire qui" No, non lo è. Si potrebbe fare in modo che un'applicazione non lo fa inserto null. Ma ho psql (per esempio) e posso inserire valori nulli sia intenzionalmente che accidentalmente senza che l'applicazione lo sappia.
Mike Sherrill "Cat Recall",

5
L'unica applicazione che può assicurarsi che nessuno modifichi manualmente la tabella è il dbms stesso.
Mike Sherrill "Cat Recall",

Risposte:


9

Cosa succede quando arriva un nuovo programmatore e deve scrivere un'app contro quel db? Non sanno che il campo x deve essere NOT NULL.

Un altro programma potrebbe supporre che tutti i campi x siano NOT NULLper eseguire conteggi, ma alcuni ora sono a NULLcausa del nuovo programma, portando a errori incoerenti e difficili da rintracciare.

IMHO è sempre meglio applicare le regole di integrità dei dati il ​​più vicino possibile ai dati, cioè nel database. In questo modo, nuove app e / o programmatori non possono confondere i tuoi dati.

Programmatori, applicazioni, lingue e framework vanno e vengono. Dati e database tendono a persistere. Il database è la tua ultima linea di difesa contro dati incoerenti, potenzialmente errati.

Sfruttare al massimo i meccanismi di imposizione dei vincoli di integrità del database, anche a spese delle prestazioni. Un sistema lento che produce risultati corretti è infinitamente superiore a uno veloce che sbaglia le cose!


1
IMHO it is always best to enforce data integrity rules as near to the data as possiblequesto è in realtà lo stesso del sentimento di cui ho scritto. E questo è esattamente il motivo per cui sto cercando giustificazioni reali. Abbiamo una revisione del codice in atto e una buona documentazione, quindi le preoccupazioni su un nuovo sviluppatore che non sa qualcosa non sono sufficienti per giustificare la modifica.
Salvador Dali,

4
Revisioni del codice e buona documentazione non ti garantiscono contro (programmazione o altri) errori.
ypercubeᵀᴹ

2
E quanti hanno REAL PROGRAMMERSletto tutta (o anche qualsiasi) la documentazione prima di rimanere bloccati in un progetto in cui si trovano in una scadenza serrata?
Vérace,

3
Una volta ho fatto una recensione in una banca che aveva lo stesso atteggiamento per il loro data warehouse. Nel loro caso - nessuna integrità referenziale. Bene, succede che il 40% dei dati più vecchi era spazzatura perché qualcuno non aveva letto la documentazione e cancellato i dati nelle tabelle di ricerca. Non ti fidi delle revisioni del codice e della documentazione con integrità dei dati, ma lo rendi esplicito nel database.
TomTom,

5

Come già citato da altri nei commenti, l'aggiunta NOT NULLalle specifiche della tabella può migliorare in modo significativo le prestazioni delle query (oltre alle ottime ragioni metodologiche indicate in un'altra risposta).

Il motivo è che Query Optimizer, sapendo che una colonna non può avere un NULLvalore, può escludere test speciali per tali valori, come nel caso NOT INvs. NOT EXISTSAd esempio, puoi vedere questo blog , dove viene mostrato che non dichiarare un campo NOT NULL(quando la tabella contiene sempre valori non nulli) con una certa query aumenta il tempo di esecuzione del 500%. Il risultato è mostrato per SQL Server, ma un comportamento simile potrebbe essere presente in altri DBMS relazionali, come il tuo (per non parlare del fatto che il tuo database potrebbe essere portato su altri sistemi). Una regola generale che si può presumere è che quando più informazioni sono disponibili per Query Optimizer, è possibile produrre piani di accesso più efficienti.


Grazie. Questo è il tipo di risposta che stavo cercando.
Salvador Dali,

5
Le colonne che non contengono mai NULL, dovrebbero essere definite NOT NULLper molteplici ragioni, nessun argomento al riguardo. Ma il link al blog su SQL Server non è applicabile per Postgres e non dimostra alcuna delle implicazioni sulle prestazioni menzionate. Non dire che non ce ne sono, ma mi piacerebbe vedere prove concrete .
Erwin Brandstetter,

@ErwinBrandstetter, ho avuto aspettative molto alte sull'ottimizzatore PostgreSQL :( Dopo diversi test non ho trovato differenze significative nella query NOT IN presentata nel blog in PostgreSQL con e senza un vincolo NOT NULL. Quindi, ho cambiato la risposta e ti sto chiedendo se pensi che dovrei eliminarlo del tutto.
Renzo,

No, non penso che dovrebbe essere eliminato. Ha 5 + voti e nessun voto negativo, per uno.
ypercubeᵀᴹ

La semantica di not inper le colonne nullable è diversa anche se quindi ci deve essere qualche differenza nel piano tra i due?
Martin Smith,

2

Implicazioni spaziali

Le implicazioni spaziali sono discusse in questo post da @Erwin Brandstetter

In breve, si salverà un totalColumns - 8bit arrotondato per eccesso al byte (o MAXALIGN) più vicino , se il database ha

  1. Più di 8 colonne
  2. TUTTE le colonne sul tavolo sonoNOT NULL

Implicazioni sulle prestazioni

Tuttavia, in questo post su SE di @Erwin Brandstetter , afferma

  1. "L'impostazione di NOT NULL non ha alcun effetto di per sé sulle prestazioni. Alcuni cicli per il controllo - irrilevante."
  2. "... utilizzando effettivamente i NULL anziché i valori fittizi. A seconda dei tipi di dati, puoi risparmiare molto spazio su disco e RAM, velocizzando così ... tutto."

@Renzo ha una risposta che parla delle implicazioni delle prestazioni - suppongo che nulla di tutto ciò sia applicabile a PostgreSQL . Non trovo nulla che sostenga nulla di ciò come rilevante per PostgreSQL. Qualunque ciclo venga salvato, non può essere quantificato nemmeno nella query più rudimentale.

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;

Inoltre ho eseguito alcuni test per vedere se gli indici NULL fossero mai più veloci e non sono riuscito a confermarlo. Puoi trovare questo thread incredibilmente utile di Scott Marlowe nelle mailing list che parlano del pianificatore di query in 9.1 in grado di usare un indice parziale su clausole WHERE diverse. Ho provato questo eseguendo il seguente

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
;

Ora ho creato gli indici,

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

In entrambi questi casi il planner è stato in grado di utilizzare l'indice durante la selezione = 10e ha utilizzato una scansione seq durante la ricerca di NULL o 0 rispettivamente. Entrambi gli indici parziali avevano le stesse dimensioni. E gli indici completi (non mostrati) avevano le stesse dimensioni. Seguendo la stessa metodologia ho caricato la tabella con una sequenza di 1..1e5, un valore null / 0 e un'altra sequenza di 1..1e5. Entrambi i metodi sono stati in grado di trovare null / 0 con un indice che copre l'intera tabella.

TLDR; Sommario

Non posso dimostrare nulla in un modo o nell'altro sulla maggior parte delle preoccupazioni relative alle prestazioni che pensavo valessero la pena testare per includere l'insufficienza del pianificatore. Il vantaggio di usare null per salvare ram è reale. Lo spazio su disco risparmiato non utilizzando null è trascurabile e si tratta di un'esagerazione eccessiva su tabelle con una NULLABLEcolonna o meno di 8 colonne. In questi casi non c'è spazio su disco risparmiato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.