Question

J'utilise SQL Server 2005. Je souhaite contraindre les valeurs d'une colonne à être uniques, tout en autorisant NULLS.

Ma solution actuelle implique un index unique sur une vue comme celle-ci:

CREATE VIEW vw_unq WITH SCHEMABINDING AS
    SELECT Column1
      FROM MyTable
     WHERE Column1 IS NOT NULL

CREATE UNIQUE CLUSTERED INDEX unq_idx ON vw_unq (Column1)

Avez-vous de meilleures idées?

Était-ce utile?

La solution

Vous ne pouvez certainement pas faire cela, car cela enfreint le but des uniques.

Cependant, cette personne semble avoir un travail décent dans les environs: http://sqlservercodebook.blogspot.com /2008/04/multiple-null-values-in-unique-index-in.html

Autres conseils

SQL Server 2008 permet de créer un index filtré: http: / /msdn.microsoft.com/en-us/library/cc280372.aspx . (Je vois que Simon a ajouté ceci en tant que commentaire, mais a pensé qu'elle méritait sa propre réponse car ce commentaire est facilement oublié.)

Une autre option est un déclencheur pour vérifier l'unicité, mais cela pourrait affecter les performances.

L'astuce de colonne calculée est largement connue sous le nom de "nullbuster" ;; mes notes créditent Steve Kass:

CREATE TABLE dupNulls (
pk int identity(1,1) primary key,
X  int NULL,
nullbuster as (case when X is null then pk else 0 end),
CONSTRAINT dupNulls_uqX UNIQUE (X,nullbuster)
)

À proprement parler, une colonne nullable unique (ou un ensemble de colonnes) ne peut être NULL (ou un enregistrement de valeurs NULL) une seule fois, car le fait d'avoir la même valeur (et cela inclut NULL) plus d'une fois viole manifestement la contrainte unique.

Toutefois, cela ne signifie pas le concept de "colonnes uniques nullables". est valable; pour réellement l'implémenter dans toute base de données relationnelle, nous devons simplement garder à l'esprit que ce type de base de données est censé être normalisé pour fonctionner correctement, et que la normalisation implique généralement l'ajout de plusieurs tables supplémentaires (non-entités) pour établir des relations entre les entités. .

Travaillons un exemple de base en considérant une seule "colonne unique nullable", il est facile de l'étendre à d'autres colonnes.

Supposons que les informations représentées par une table comme celle-ci:

create table the_entity_incorrect
(
  id integer,
  uniqnull integer null, /* we want this to be "unique and nullable" */
  primary key (id)
);

Nous pouvons le faire en mettant uniqnull à part et en ajoutant un deuxième tableau pour établir une relation entre des valeurs uniqnull et the_entity (plutôt que d'avoir uniqnull "dedans" et "the_entity"):

create table the_entity
(
  id integer,
  primary key(id)
);

create table the_relation
(
  the_entity_id integer not null,
  uniqnull integer not null,

  unique(the_entity_id),
  unique(uniqnull),
  /* primary key can be both or either of the_entity_id or uniqnull */
  primary key (the_entity_id, uniqnull), 
  foreign key (the_entity_id) references the_entity(id)
);

Pour associer une valeur d'uniqnull à une ligne de l'entité, vous devez également ajouter une ligne à la_relation.

Pour les lignes dans the_entity où aucune valeur uniqnull n’est associée (c’est-à-dire pour celles que nous placerions NULL dans the_entity_incorrect), nous n’ajoutons tout simplement pas une ligne dans la_relation.

Notez que les valeurs pour uniqnull seront uniques pour toutes les relations the_relation, et notez également que pour chaque valeur de l'entité_entité, il peut exister au plus une valeur dans la relation_realation, puisque les clés primaire et étrangère qui y sont associées l'appliquent.

Ensuite, si une valeur de 5 pour uniqnull doit être associée à un identifiant the_entity de 3, nous devons:

start transaction;
insert into the_entity (id) values (3); 
insert into the_relation (the_entity_id, uniqnull) values (3, 5);
commit;

Et si une valeur d'identifiant de 10 pour the_entity n'a pas d'équivalent unique, nous ne faisons que:

start transaction;
insert into the_entity (id) values (10); 
commit;

Pour dénormaliser ces informations et obtenir les données qu'une table telle que la_entité_incorrect pourrait contenir, nous devons:

select
  id, uniqnull
from
  the_entity left outer join the_relation
on
  the_entity.id = the_relation.the_entity_id
;

La " jointure externe gauche " L’opérateur s’assure que toutes les lignes de the_entity apparaissent dans le résultat, en mettant NULL dans la colonne uniqnull quand aucune colonne correspondante n’est présente dans the_relation.

N'oubliez pas que tous les efforts déployés pendant quelques jours (ou semaines ou mois) pour concevoir une base de données bien normalisée (ainsi que les vues et procédures de dénormalisation correspondantes) vous feront économiser des années (ou des décennies) de douleur et de ressources gaspillées.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top