Fondamentalement, rien ne va pas avec un NULL dans une clé primaire multi-colonnes. Mais en avoir un a des implications que le concepteur n'avait probablement pas l'intention, c'est pourquoi de nombreux systèmes génèrent une erreur lorsque vous essayez ceci.
Prenons le cas des versions de module / package stockées sous la forme d'une série de champs:
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));
Les 5 premiers éléments de la clé primaire sont des parties régulièrement définies d'une version finale, mais certains packages ont une extension personnalisée qui n'est généralement pas un entier (comme "rc-foo" ou "vanilla" ou "beta" ou quoi que ce soit d'autre pour que quatre champs sont insuffisants pourraient imaginer). Si un paquet n'a pas d'extension, alors il est NULL dans le modèle ci-dessus, et aucun mal ne serait fait en laissant les choses de cette façon.
Mais qu'est - ce qu'un NULL? Il est censé représenter un manque d'information, une inconnue. Cela dit, cela a peut-être plus de sens:
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));
Dans cette version, la partie «ext» du tuple n'est PAS NULL mais par défaut une chaîne vide - qui est sémantiquement (et pratiquement) différente d'un NULL. Un NULL est une inconnue, alors qu'une chaîne vide est un enregistrement délibéré de "quelque chose qui n'est pas présent". En d'autres termes, «vide» et «nul» sont des choses différentes. C'est la différence entre «Je n'ai pas de valeur ici» et «Je ne sais pas quelle est la valeur ici».
Lorsque vous enregistrez un package qui n'a pas d'extension de version, vous savez qu'il n'a pas d'extension, donc une chaîne vide est en fait la valeur correcte. Un NULL ne serait correct que si vous ne saviez pas s'il avait une extension ou non, ou si vous saviez qu'il le faisait mais ne saviez pas ce que c'était. Cette situation est plus facile à gérer dans les systèmes où les valeurs de chaîne sont la norme, car il n'y a aucun moyen de représenter un "entier vide" autre que l'insertion de 0 ou 1, qui finira par être cumulé dans toutes les comparaisons effectuées plus tard (ce qui a ses propres implications) *.
Incidemment, les deux méthodes sont valides dans PostgreSQL (puisque nous parlons de RDMBS "entreprise"), mais les résultats de la comparaison peuvent varier un peu lorsque vous ajoutez un NULL au mélange - parce que NULL == "ne sais pas" donc tout les résultats d'une comparaison impliquant une liquidation NULL étant NULL puisque vous ne pouvez pas savoir quelque chose d'inconnu. DANGER! Réfléchissez bien à cela: cela signifie que les résultats de comparaison NULL se propagent à travers une série de comparaisons. Cela peut être une source de bugs subtils lors du tri, de la comparaison, etc.
Postgres suppose que vous êtes un adulte et peut prendre cette décision vous-même. Oracle et DB2 supposent que vous ne vous êtes pas rendu compte que vous faisiez quelque chose de stupide et lancent une erreur. C'est généralement la bonne chose, mais pas toujours - vous pourriez en fait ne pas savoir et avoir un NULL dans certains cas et donc laisser une ligne avec un élément inconnu contre lequel des comparaisons significatives sont impossibles est un comportement correct.
Dans tous les cas, vous devez vous efforcer d'éliminer le nombre de champs NULL que vous autorisez sur l'ensemble du schéma et doublement lorsqu'il s'agit de champs qui font partie d'une clé primaire. Dans la grande majorité des cas, la présence de colonnes NULL est une indication d'une conception de schéma non normalisée (par opposition à une conception délibérément dénormalisée) et doit faire l'objet d'une réflexion approfondie avant d'être acceptée.
[* NOTE: Il est possible de créer un type personnalisé qui est l'union d'entiers et un type "bas" qui signifierait sémantiquement "vide" par opposition à "inconnu". Malheureusement, cela introduit un peu de complexité dans les opérations de comparaison et le fait d'être vraiment correct en général ne vaut pas la peine en pratique, car vous ne devriez pas avoir du tout autorisé à avoir beaucoup de NULL
valeurs en premier lieu. Cela dit, ce serait merveilleux si les SGBDR incluaient un BOTTOM
type par défaut en plus de NULL
pour éviter l'habitude de confondre avec désinvolture la sémantique de "aucune valeur" avec "valeur inconnue". ]