Techniquement, NULL = NULL est False, par cette logique, NULL n'est égal à aucun NULL et tous les NULL sont distincts. Cela ne signifie-t-il pas que tous les NULL sont uniques et qu'un index unique devrait permettre un nombre quelconque de NULL?
Techniquement, NULL = NULL est False, par cette logique, NULL n'est égal à aucun NULL et tous les NULL sont distincts. Cela ne signifie-t-il pas que tous les NULL sont uniques et qu'un index unique devrait permettre un nombre quelconque de NULL?
Réponses:
Pourquoi ça marche comme ça? En effet, il y a bien longtemps, une personne avait pris une décision de conception sans savoir ni se soucier de ce que disait la norme (après tout, nous avons toutes sortes de comportements étranges avec NULL
s et nous pouvons contraindre différents comportements à volonté). Cette décision a dicté que, dans ce cas NULL = NULL
,.
Ce n'était pas une décision très intelligente. Ce qu’ils auraient dû faire, c’est que le comportement par défaut soit conforme à la norme ANSI, et s’ils le souhaitaient vraiment, autorisez-le via une option DDL telle que WITH CONSIDER_NULLS_EQUAL
ou WITH ALLOW_ONLY_ONE_NULL
.
Bien sûr, le recul est 20/20.
Et nous avons une solution de contournement, maintenant, de toute façon, même si ce n’est pas la plus propre ou la plus intuitive.
Vous pouvez obtenir le comportement ANSI approprié dans SQL Server 2008 et versions ultérieures en créant un index filtré unique.
CREATE UNIQUE INDEX foo ON dbo.bar(key) WHERE key IS NOT NULL;
Cela autorise plusieurs NULL
valeurs car ces lignes sont complètement exclues de la vérification des doublons. En prime, il s'agirait d'un index plus petit que celui qui comprenait la table entière si plusieurs NULL
s étaient autorisés (en particulier si ce n'est pas la seule colonne de l'index, elle contient des INCLUDE
colonnes, etc.). Toutefois, vous souhaiterez peut-être connaître certaines des autres limitations des index filtrés:
Correct. L'implémentation d'une contrainte unique ou d'un index dans le serveur SQL autorise un et un seul NULL. Également correct, cela ne correspond techniquement pas à la définition de NULL, mais c’est une des choses qu’ils ont faite pour le rendre plus utile même s’il n’est pas "techniquement" correct. Notez qu'une PRIMARY KEY (également un index unique) n'autorise pas les valeurs NULL (bien sûr).
Tout d’abord, arrêtez d’utiliser l’expression "valeur nulle", elle vous égarera. Au lieu de cela, utilisez l'expression "marqueur null" - un marqueur dans une colonne indiquant que la valeur réelle de cette colonne est soit manquante, soit inapplicable (mais notez que le marqueur ne dit pas laquelle de ces options est réellement le cas¹).
Imaginons maintenant ce qui suit (lorsque la base de données n’a pas une connaissance complète de la situation modélisée).
Situation Database
ID Code ID Code
-- ----- -- -----
1 A 1 A
2 B 2 (null)
3 C 3 C
4 B 4 (null)
La règle d'intégrité que nous modélisons est "le code doit être unique". La situation réelle enfreint cette règle. La base de données ne devrait donc pas autoriser la présence simultanée des éléments 2 et 4 dans la table.
L'approche la plus sûre et la moins flexible consiste à interdire les marqueurs nuls dans le champ Code. Il n'y a donc aucune possibilité de données incohérentes. L'approche la plus flexible consisterait à autoriser plusieurs marqueurs nuls et à s'inquiéter de l'unicité lorsque les valeurs sont entrées.
Les programmeurs de Sybase ont opté pour une approche relativement sûre et peu flexible, consistant à ne permettre qu'un seul marqueur null dans la table - ce dont les commentateurs se plaignent depuis. Microsoft a continué ce comportement, je suppose pour la compatibilité en amont.
¹ Je suis sûr d’avoir lu quelque part que Codd envisageait d’implémenter deux marqueurs nuls - un pour l’inconnu, un pour inapplicable - mais l’avait rejeté, mais je n’ai pas trouvé la référence. Est-ce que je me souviens correctement?
PS Ma citation préférée sur null: Louis Davidson, «Conception de base de données SQL Server 2000 professionnelle», Wrox Press, 2001, page 52. «Réduit à une seule phrase: NULL est diabolique».
null
n'atteint pas cet objectif non plus. Parce que la valeur manquante peut s'avérer identique à la valeur de l'une des autres lignes.
CHECK (Value IN ('A','B','C','D'))
? Ensuite, l'implémentation de SQL-Server et le standard SQL autorisent la table à comporter 5 lignes (une ligne pour chaque valeur plus 1 avec NULL.) Ensuite, même si la base de données est cohérente avec ses contraintes, elle ne correspond pas à l'intention du concepteur la table doit avoir un maximum de 4 lignes. Il n'y a pas de valeur dans laquelle la valeur NULL puisse être modifiée qui ne violera pas une contrainte, sauf si une ou plusieurs lignes sont supprimées.
CREATE TABLE #T(A INT NULL UNIQUE);INSERT INTO #T VALUES (1),(NULL);UPDATE #T SET A = 1 WHERE A IS NULL;
va soulever une erreur. Selon votre théorie des motivations de conception, cela aurait dû empêcher l’insertion de NULL
dans le premier cas - parce que la connaissance incomplète signifie qu’il n’ya aucune garantie que la valeur soit différente.
Cela n’est peut-être pas exact sur le plan technique, mais philosophiquement, il m’aide à dormir la nuit ...
Comme plusieurs autres l'ont dit ou y ont fait allusion, si vous pensez que NULL est inconnu, vous ne pouvez pas déterminer si une valeur NULL est en fait égale à une autre valeur NULL. En pensant de cette façon, l'expression NULL == NULL doit être évaluée à NULL, ce qui signifie inconnue.
Une contrainte unique nécessiterait une valeur définitive pour la comparaison des valeurs de colonne. En d'autres termes, lorsque vous comparez une valeur de colonne unique à une autre valeur de colonne à l'aide de l'opérateur d'égalité, elle doit être évaluée à false pour être valide. Inconnu n'est pas vraiment faux même s'il est souvent traité comme de la fausseté. Deux valeurs NULL peuvent être égales, ou non ... cela ne peut tout simplement pas être déterminé définitivement.
Il est utile de considérer une contrainte unique comme une restriction des valeurs pouvant être considérées comme distinctes les unes des autres. Ce que je veux dire par là, c'est si vous exécutez un SELECT qui ressemble à ceci:
SELECT * from dbo.table1 WHERE ColumnWithUniqueContraint="some value"
La plupart des gens s'attendent à un résultat, étant donné qu'il existe une contrainte unique. Si vous autorisiez plusieurs valeurs NULL dans ColumnWithUniqueConstraint, il serait impossible de sélectionner une seule ligne distincte de la table en utilisant NULL comme valeur comparée.
Cela étant dit, j’estime que, qu’elle soit mise en œuvre avec précision ou non, conformément à la définition de NULL, il est nettement plus pratique dans la plupart des situations que d’autoriser plusieurs valeurs NULL.
L'un des principaux objectifs d'une UNIQUE
contrainte est d'empêcher les enregistrements en double. S'il est nécessaire d'avoir une table dans laquelle il peut exister plusieurs enregistrements pour lesquels une valeur est "inconnue", mais que deux enregistrements ne sont pas autorisés à avoir la même valeur "connue", les identificateurs artificiels uniques doivent être attribués aux valeurs inconnues avant d'être ajouté à la table.
Il existe quelques rares cas dans lesquels une colonne qui a une UNIQUE
contrainte et contient une seule valeur null; Par exemple, si une table contient un mappage entre les valeurs de colonne et les descriptions de texte localisées, une ligne pour NULL
permettrait de définir la description qui devrait apparaître lorsque cette colonne est présente dans une autre table NULL
. Le comportement de NULL
permet ce cas d'utilisation.
Sinon, je ne vois aucune base pour une base de données avec une UNIQUE
contrainte sur une colonne pour permettre l'existence de nombreux enregistrements identiques, mais je ne vois aucun moyen d'empêcher cela tout en permettant à plusieurs enregistrements dont les valeurs de clé ne sont pas distinguables. Déclarer que ce NULL
n'est pas égal à lui-même ne rendra pas les NULL
valeurs distinctes les unes des autres.