Je veux avoir une contrainte unique sur une colonne que je vais remplir avec des GUID. Cependant, mes données contiennent des valeurs nulles pour ces colonnes. Comment créer la contrainte qui autorise plusieurs valeurs nulles?
Voici un exemple de scénario . Considérez ce schéma:
CREATE TABLE People (
Id INT CONSTRAINT PK_MyTable PRIMARY KEY IDENTITY,
Name NVARCHAR(250) NOT NULL,
LibraryCardId UNIQUEIDENTIFIER NULL,
CONSTRAINT UQ_People_LibraryCardId UNIQUE (LibraryCardId)
)
Ensuite, voyez ce code pour ce que j'essaie de réaliser:
-- This works fine:
INSERT INTO People (Name, LibraryCardId)
VALUES ('John Doe', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');
-- This also works fine, obviously:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marie Doe', 'BBBBBBBB-BBBB-BBBB-BBBB-BBBBBBBBBBBB');
-- This would *correctly* fail:
--INSERT INTO People (Name, LibraryCardId)
--VALUES ('John Doe the Second', 'AAAAAAAA-AAAA-AAAA-AAAA-AAAAAAAAAAAA');
-- This works fine this one first time:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Richard Roe', NULL);
-- THE PROBLEM: This fails even though I'd like to be able to do this:
INSERT INTO People (Name, LibraryCardId)
VALUES ('Marcus Roe', NULL);
La déclaration finale échoue avec un message:
Violation de la contrainte UNIQUE KEY 'UQ_People_LibraryCardId'. Impossible d'insérer une clé en double dans l'objet 'dbo.People'.
Comment puis-je modifier mon schéma et / ou ma contrainte d'unicité afin qu'elle autorise plusieurs NULL
valeurs, tout en vérifiant l'unicité des données réelles?
sql-server
tsql
Stuart
la source
la source
null
n'est pas une valeur mais l'absence de valeur. Selon la norme SQL,null
n'est pas considéré comme égal ànull
. Alors pourquoi le multiplenull
devrait être une violation d'unicité?Réponses:
SQL Server 2008 +
Vous pouvez créer un index unique qui accepte plusieurs NULL avec une
WHERE
clause. Voir la réponse ci-dessous .Avant SQL Server 2008
Vous ne pouvez pas créer une contrainte UNIQUE et autoriser les valeurs NULL. Vous devez définir une valeur par défaut de NEWID ().
Mettez à jour les valeurs existantes vers NEWID () où NULL avant de créer la contrainte UNIQUE.
la source
Ce que vous recherchez fait en effet partie des normes ANSI SQL: 92, SQL: 1999 et SQL: 2003, c'est-à-dire qu'une contrainte UNIQUE doit interdire les valeurs non NULL en double mais accepter plusieurs valeurs NULL.
Dans le monde Microsoft de SQL Server cependant, un seul NULL est autorisé mais plusieurs NULL ne le sont pas ...
Dans SQL Server 2008 , vous pouvez définir un index filtré unique basé sur un prédicat qui exclut les valeurs NULL:
Dans les versions antérieures, vous pouvez recourir à VIEWS avec un prédicat NOT NULL pour appliquer la contrainte.
la source
SQL Server 2008 et versions ultérieures
Il suffit de filtrer un index unique:
Dans les versions inférieures, une vue matérialisée n'est toujours pas requise
Pour SQL Server 2005 et versions antérieures, vous pouvez le faire sans vue. Je viens d'ajouter une contrainte unique comme celle que vous demandez à l'une de mes tables. Étant donné que je souhaite l'unicité de la colonne
SamAccountName
, mais que je souhaite autoriser plusieurs valeurs NULL, j'ai utilisé une colonne matérialisée plutôt qu'une vue matérialisée:Vous devez simplement mettre quelque chose dans la colonne calculée qui sera garanti unique sur toute la table lorsque la colonne unique souhaitée réelle est NULL. Dans ce cas,
PartyID
est une colonne d'identité et être numérique ne correspondra à aucunSamAccountName
, donc cela a fonctionné pour moi. Vous pouvez essayer votre propre méthode - assurez-vous de bien comprendre le domaine de vos données afin qu'il n'y ait aucune possibilité d'intersection avec des données réelles. Cela pourrait être aussi simple que d'ajouter un caractère différenciateur comme celui-ci:Même si un jour, il
PartyID
devenait non numérique et pouvait coïncider avec unSamAccountName
, maintenant cela n'aurait plus d'importance.Notez que la présence d'un index incluant la colonne calculée entraîne implicitement l'enregistrement de chaque résultat d'expression sur le disque avec les autres données de la table, ce qui prend de l'espace disque supplémentaire.
Notez que si vous ne voulez pas d'index, vous pouvez toujours économiser de l'UC en faisant précalculer l'expression sur le disque en ajoutant le mot-clé
PERSISTED
à la fin de la définition de l'expression de colonne.Dans SQL Server 2008 et versions ultérieures, utilisez plutôt la solution filtrée si vous le pouvez!
Controverse
Veuillez noter que certains professionnels de la base de données verront cela comme un cas de «NULL de substitution», qui ont certainement des problèmes (principalement en raison de problèmes pour essayer de déterminer quand quelque chose est une valeur réelle ou une valeur de substitution pour les données manquantes ; il peut également y avoir des problèmes avec le nombre de valeurs de substitution non NULL se multipliant comme un fou).
Cependant, je crois que ce cas est différent. La colonne calculée que j'ajoute ne sera jamais utilisée pour déterminer quoi que ce soit. Il n'a aucune signification en soi et n'encode aucune information qui n'est pas déjà trouvée séparément dans d'autres colonnes correctement définies. Il ne doit jamais être sélectionné ou utilisé.
Donc, mon histoire est que ce n'est pas un NULL de substitution, et je m'y tiens! Puisque nous ne voulons pas réellement la valeur non NULL à d'autres fins que de tromper l'
UNIQUE
index pour ignorer les valeurs NULL, notre cas d'utilisation n'a aucun des problèmes qui surviennent avec la création NULL de substitution normale.Cela dit, je n'ai pas de problème à utiliser une vue indexée à la place, mais cela pose certains problèmes, comme l'exigence d'utilisation
SCHEMABINDING
. Amusez-vous à ajouter une nouvelle colonne à votre table de base (vous devrez au minimum supprimer l'index, puis supprimer la vue ou modifier la vue pour ne pas être liée au schéma). Consultez la liste complète (longue) des exigences pour la création d'une vue indexée dans SQL Server (2005) (également les versions ultérieures), (2000) .Mise à jour
Si votre colonne est numérique, il peut être difficile de vous assurer que la contrainte unique utilisant
Coalesce
n'entraîne pas de collisions. Dans ce cas, il existe quelques options. L'une pourrait consister à utiliser un nombre négatif, à ne mettre les «NULL de substitution» que dans la plage négative et les «valeurs réelles» uniquement dans la plage positive. Alternativement, le modèle suivant pourrait être utilisé. Dans le tableauIssue
(oùIssueID
est lePRIMARY KEY
), il peut y avoir ou non unTicketID
, mais s'il y en a un, il doit être unique.Si IssueID 1 a le ticket 123, la
UNIQUE
contrainte sera sur les valeurs (123, NULL). Si IssueID 2 n'a pas de ticket, il sera activé (NULL, 2). Certaines réflexions montreront que cette contrainte ne peut être dupliquée pour aucune ligne du tableau et autorise toujours plusieurs NULL.la source
Pour les personnes qui utilisent Microsoft SQL Server Manager et souhaitent créer un index unique mais nul, vous pouvez créer votre index unique comme vous le feriez normalement dans vos propriétés d'index pour votre nouvel index, sélectionnez "Filtrer" dans le panneau de gauche, puis entrez votre filtre (qui est votre clause where). Il devrait lire quelque chose comme ceci:
Cela fonctionne avec MSSQL 2012
la source
Lorsque j'ai appliqué l'index unique ci-dessous:
chaque mise à jour et insertion non nulle a échoué avec l'erreur ci-dessous:
J'ai trouvé cela sur MSDN
Donc, pour que cela fonctionne correctement, je l'ai fait
Je pense qu'il est possible de définir cette option dans le code en utilisant
mais je n'ai pas testé ça
la source
Créez une vue qui sélectionne uniquement les non-
NULL
colonnes et créez laUNIQUE INDEX
sur la vue:Notez que vous devrez effectuer des
INSERT
«etUPDATE
» sur la vue au lieu de la table.Vous pouvez le faire avec un
INSTEAD OF
déclencheur:la source
Cela peut aussi être fait dans le concepteur
Faites un clic droit sur l'index> Propriétés pour obtenir cette fenêtre
la source
Il est possible de créer une contrainte unique sur une vue indexée en cluster
Vous pouvez créer la vue comme ceci:
et la contrainte unique comme celle-ci:
la source
Pensez peut-être à un
INSTEAD OF
déclencheur " " et faites-le vous-même? Avec un index non clusterisé (non unique) sur la colonne pour activer la recherche.la source
Comme indiqué précédemment, SQL Server n'implémente pas la norme ANSI en ce qui concerne
UNIQUE CONSTRAINT
. Il existe un ticket sur Microsoft Connect pour cela depuis 2007. Comme suggéré ici et ici, les meilleures options à ce jour sont d'utiliser un index filtré comme indiqué dans une autre réponse ou une colonne calculée, par exemple:la source
Vous pouvez créer un déclencheur INSTEAD OF pour vérifier les conditions spécifiques et les erreurs si elles sont remplies. La création d'un index peut être coûteuse sur des tables plus grandes.
Voici un exemple:
la source
Vous ne pouvez pas le faire avec une
UNIQUE
contrainte, mais vous pouvez le faire dans un déclencheur.la source
la source
ce code si vous faites un formulaire d'inscription avec textBox et utilisez insert et ur textBox est vide et u cliquez sur le bouton soumettre.
la source