ORACLE n'autorise les valeurs NULL dans aucune des colonnes qui comprennent une clé primaire. Il semble qu'il en soit de même pour la plupart des autres systèmes «au niveau de l'entreprise».
Dans le même temps, la plupart des systèmes autorisent également des contraintes uniques sur les colonnes Nullable.
Pourquoi les contraintes uniques peuvent-elles avoir des valeurs NULL mais pas les clés primaires? Y a-t-il une raison logique fondamentale à cela, ou s'agit-il davantage d'une limitation technique?
database
database-design
Roman Starkov
la source
la source
Réponses:
Les clés primaires servent à identifier les lignes de manière unique. Cela se fait en comparant toutes les parties d'une clé à l'entrée.
Par définition, NULL ne peut pas faire partie d'une comparaison réussie. Même une comparaison avec elle-même (
NULL = NULL
) échouera. Cela signifie qu'une clé contenant NULL ne fonctionnerait pas.De plus, NULL est autorisé dans une clé étrangère, pour marquer une relation facultative. (*) L' autoriser également dans le PK briserait cela.
(*) Un mot d'avertissement: avoir des clés étrangères Nullable n'est pas une conception de base de données relationnelle propre.
S'il y a deux entités
A
etB
oùA
peut éventuellement être liéB
, la solution propre est de créer une table de résolution (disonsAB
). Ce tableau serait un lienA
avecB
: S'il est une relation alors il contiendrait un enregistrement, s'il est pas alors il ne serait pas.la source
Une clé primaire définit un identifiant unique pour chaque ligne d'une table: lorsqu'une table a une clé primaire, vous disposez d'un moyen garanti de sélectionner n'importe quelle ligne à partir de celle-ci.
Une contrainte unique n'identifie pas nécessairement chaque ligne; il précise juste que si une ligne a des valeurs dans ses colonnes, alors ils doivent être uniques. Cela ne suffit pas pour identifier de manière unique chaque ligne, ce que doit faire une clé primaire.
la source
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:
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:
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 unBOTTOM
type par défaut en plus deNULL
pour éviter l'habitude de confondre avec désinvolture la sémantique de "aucune valeur" avec "valeur inconnue". ]la source
NULL == NULL -> false (au moins dans les SGBD)
Vous ne pourrez donc pas récupérer de relations en utilisant une valeur NULL, même avec des colonnes supplémentaires avec des valeurs réelles.
la source
where pk_1 = 'a' and pk_2 = 'b'
avec des valeurs normales et basculer vers des valeurswhere pk_1 is null and pk_2 = 'b'
nulles.where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))
/La réponse de Tony Andrews est décente. Mais la vraie réponse est que cela a été une convention utilisée par la communauté des bases de données relationnelles et n'est PAS une nécessité. C'est peut-être une bonne convention, peut-être pas.
Comparer quoi que ce soit à NULL entraîne INCONNU (3e valeur de vérité). Ainsi, comme cela a été suggéré avec des nulls, toute la sagesse traditionnelle concernant l'égalité est abandonnée. Eh bien, c'est ce que cela semble à première vue.
Mais je ne pense pas que ce soit nécessairement le cas et même les bases de données SQL ne pensent pas que NULL détruit toute possibilité de comparaison.
Exécutez dans votre base de données la requête SELECT * FROM VALUES (NULL) UNION SELECT * FROM VALUES (NULL)
Ce que vous voyez est juste un tuple avec un attribut qui a la valeur NULL. Donc, l'union a reconnu ici les deux valeurs NULL comme égales.
Lors de la comparaison d'une clé composite qui a 3 composants à un tuple avec 3 attributs (1, 3, NULL) = (1, 3, NULL) <=> 1 = 1 AND 3 = 3 AND NULL = NULL Le résultat de ceci est INCONNU .
Mais nous pourrions définir un nouveau type d'opérateur de comparaison par exemple. ==. X == Y <=> X = Y OU (X EST NULL ET Y EST NULL)
Avoir ce type d'opérateur d'égalité rendrait les clés composites avec des composants nuls ou une clé non composite avec une valeur nulle sans problème.
la source
Je pense toujours que c'est un défaut fondamental / fonctionnel provoqué par une technicité. Si vous avez un champ facultatif par lequel vous pouvez identifier un client, vous devez maintenant pirater une valeur fictive, simplement parce que NULL! = NULL, pas particulièrement élégant mais c'est un "standard de l'industrie"
la source