Fondamentalmente, nulla è sbagliato in un NULL in una chiave primaria multi-colonna. Ma avendo uno ha implicazioni che il progettista probabilmente non intendeva, motivo per cui molti sistemi generano un errore quando provi questo.
Si consideri il caso delle versioni del modulo / pacchetto memorizzate come una serie di campi:
CREATE TABLE module
(name varchar(20) PRIMARY KEY,
description text DEFAULT '' NOT NULL);
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20),
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
I primi 5 elementi della chiave primaria sono parti definite regolarmente di una versione di rilascio, ma alcuni pacchetti hanno un'estensione personalizzata che di solito non è un numero intero (come "rc-foo" o "vanilla" o "beta" o qualsiasi altra cosa per cui qualcuno chi quattro campi è insufficiente potrebbe pensare). Se un pacchetto non ha un'estensione, allora è NULL nel modello sopra e non si danneggerebbe lasciando le cose in quel modo.
Ma che cosa è un NULL? Dovrebbe rappresentare una mancanza di informazioni, uno sconosciuto. Detto questo, forse questo ha più senso:
CREATE TABLE version
(module varchar(20) REFERENCES module,
major integer NOT NULL,
minor integer DEFAULT 0 NOT NULL,
patch integer DEFAULT 0 NOT NULL,
release integer DEFAULT 1 NOT NULL,
ext varchar(20) DEFAULT '' NOT NULL,
notes text DEFAULT '' NOT NULL,
PRIMARY KEY (module, major, minor, patch, release, ext));
In questa versione la parte "ext" della tupla NON è NULL ma per impostazione predefinita è una stringa vuota - che è semanticamente (e praticamente) diversa da un NULL. Un NULL è sconosciuto, mentre una stringa vuota è una registrazione deliberata di "qualcosa che non è presente". In altre parole, "vuoto" e "null" sono cose diverse. È la differenza tra "Non ho un valore qui" e "Non so quale sia il valore qui".
Quando registri un pacchetto privo di un'estensione di versione, sai che manca un'estensione, quindi una stringa vuota è in realtà il valore corretto. Un NULL sarebbe corretto solo se non sapessi se avesse un'estensione o no, o sapevi che lo era, ma non sapevi cosa fosse. Questa situazione è più facile da gestire nei sistemi in cui i valori delle stringhe sono la norma, perché non c'è modo di rappresentare un "numero intero vuoto" diverso dall'inserimento di 0 o 1, che finirà per essere arrotolato in tutti i confronti effettuati in seguito (che ha le sue implicazioni) *.
Per inciso, entrambi i modi sono validi in Postgres (dal momento che stiamo discutendo di RDMBS "enterprise"), ma i risultati del confronto possono variare abbastanza quando si lancia un NULL nel mix - perché NULL == "non lo so" quindi tutti i risultati di un confronto che coinvolgono un NULL finiscono per essere NULL poiché non si può sapere qualcosa di sconosciuto. PERICOLO! Pensaci bene: questo significa che i risultati del confronto NULL si propagano attraverso una serie di confronti. Questo può essere una fonte di bug sottili durante l'ordinamento, il confronto, ecc.
Postgres presume che tu sia un adulto e che tu possa prendere questa decisione da solo. Oracle e DB2 presumono che non ti rendessi conto che stavi facendo qualcosa di stupido e che avessero lanciato un errore. Questa è di solito la cosa giusta, ma non sempre - potresti effettivamente non sapere e avere un NULL in alcuni casi e quindi lasciare una riga con un elemento sconosciuto contro il quale sono impossibili confronti significativi è un comportamento corretto.
In ogni caso dovresti cercare di eliminare il numero di campi NULL che permetti nell'intero schema e doppiamente quando si tratta di campi che fanno parte di una chiave primaria. Nella stragrande maggioranza dei casi la presenza di colonne NULL è un'indicazione di progettazione dello schema non normalizzata (piuttosto che deliberatamente non normalizzata) e dovrebbe essere presa in seria considerazione prima di essere accettata.
[* NOTA: è possibile creare un tipo personalizzato che è l'unione di numeri interi e un tipo "bottom" che significherebbe semanticamente "vuoto" anziché "sconosciuto". Sfortunatamente questo introduce un po 'di complessità nelle operazioni di confronto e in genere essere veramente corretti per tipo non vale la pena in pratica poiché non dovresti prima avere molti NULL
valori. Detto questo, sarebbe meraviglioso se gli RDBMS includessero un BOTTOM
tipo predefinito oltre NULL
a prevenire l'abitudine di confondere casualmente la semantica di "nessun valore" con "valore sconosciuto". ]