2008-12-18 12 views
10

J'utilise SQL Server 2005.La valeur du champ doit être unique sauf si elle est NULL

J'ai un champ qui doit contenir une valeur unique ou une valeur NULL. Je pense que je devrais appliquer ceci avec un CHECK CONSTRAINT ou un TRIGGER for INSERT, UPDATE.

Y a-t-il un avantage à utiliser une contrainte ici sur un déclencheur (ou vice-versa)? À quoi pourrait ressembler une telle contrainte/déclencheur?

Ou existe-t-il une autre option plus appropriée que je n'ai pas envisagée?

+0

Voir aussi les réponses apportées à cette question: http://stackoverflow.com/questions/191421/how-to- create-a-unique-index-on-a-null-column –

Répondre

4

Voici une autre façon de le faire avec une contrainte. Pour appliquer cette contrainte, vous aurez besoin d'une fonction qui compte le nombre d'occurrences de la valeur du champ. Dans votre contrainte, assurez-vous simplement que ce maximum est 1.

Constraint:

field is null or dbo.fn_count_maximum_of_field(field) < 2 

EDIT Je ne me souviens pas en ce moment - et ne peut pas vérifier soit - si le contrôle de contrainte est fait avant l'insertion/mise à jour ou après. Je pense après avec l'insertion/mise à jour étant annulée en cas d'échec. S'il se trouve que je me trompe, le 2 ci-dessus devrait être 1.

Table fonction retourne un int et utilise les éléments suivants de sélection pour dériver

declare @retVal int 

    select @retVal = max(occurrences) 
    from ( 
     select field, count(*) as occurrences 
     from dbo.tbl 
     where field = @field 
     group by field 
    ) tmp 

Cela devrait être assez rapide si votre colonne comme un (non-unique) indice sur elle.

+0

J'ai corrigé cette réponse car, bien que la solution de Joe impliquant une vue indexée fonctionne et soit sans doute plus élégante, je considère que c'est une façon plus "évidente" de contraindre les données dans ma table et donc plus facile à maintenir. – kristian

+1

Cela ne fonctionnera que pour les modifications d'une seule ligne. Peut échouer pour les mises à jour multirow: http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/07/01/when-check-constraints-using-udfs-fail-for-multirow-updates.aspx –

+0

Lorsque vous dites échouer, vous voulez dire que, par erreur, une mise à jour serait illégale si tous les changements se produisaient en même temps. Je ne considère pas que ce soit autant un échec qu'un défaut du mécanisme. Un échec serait si cela permettait à la contrainte d'être violée. Le fait qu'il empêche les états intermédiaires illégaux ainsi que les états finaux est juste quelque chose que vous devez contourner. – tvanfosson

2

Dans Oracle, une clé unique autorise plusieurs valeurs NULL.

Dans SQL Server 2005, une bonne approche consiste à effectuer vos insertions via une vue et à désactiver les insertions directes dans la table.

Here is some sample code.

+1

Où est le livre blanc pour dire que c'est une bonne approche? Ceci si pour DB2 mais s'applique toujours: http://www.craigsmullins.com/viewnw.htm – gbn

2

Y at-il une clé primaire sur cette table, peut-être une colonne d'identité? Vous pouvez créer une clé unique qui est un composite du champ auquel vous appliquez l'unicité en combinaison avec la clé primaire.

Il y a une discussion à propos de tout ce genre de question ici: http://blog.sqlauthority.com/2008/09/07/sql-server-explanation-about-usage-of-unique-index-and-unique-constraint/

FYI - index SQL Server 2008 introduit qui vous filtré permettent d'aborder différemment un peu.

+0

Je ne sais pas qui et pourquoi downvoted une bonne solution. Je suis upvoting, comme cela fonctionne. –

+0

Cette réponse a probablement été rejetée en raison de la première section relative à la clé composite. Cela permettrait de dupliquer les valeurs nulles mais cela permettrait également de dupliquer des valeurs dans une colonne supposée ne contenir que des valeurs uniques. –

0

Habituellement, un déclencheur vous permettra de fournir un message plus explicite et explicatif qu'une contrainte de vérification, aussi ai-je utilisé ceux-ci pour éviter le jeu "quelle colonne était mauvaise" dans le débogage.

0

Une contrainte est beaucoup plus légère qu'un déclencheur, même si une contrainte unique est effectivement un index.

Cependant, vous n'êtes autorisé qu'une seule valeur NULL dans une contrainte/index unique. Donc, vous devrez utiliser un déclencheur pour détecter les doublons.

Il a été requested from MS to ignore NULLS, mais SQL 2008 a filtré les index (comme mentionné pendant que je tape ce)

6

Je crée une vue avec un index qui ignore les valeurs nulles via la clause where ... c'est-à-dire.Si vous insérez null dans la table, la vue ne s'en soucie pas mais si vous insérez une valeur non nulle, la vue appliquera la contrainte. Alors maintenant, ma table d'équipement a une colonne asset_tag qui autorise plusieurs valeurs nulles mais seulement des valeurs non nulles uniques.

Remarque: Si vous utilisez mssql 2000, vous devez "SET ARITHABORT ON" juste avant d'insérer, de mettre à jour ou de supprimer sur la table. Assez sûr que ce n'est pas nécessaire sur mssql 2005 et plus.

3

Vous pouvez accomplir ceci en créant une colonne calculée et mettre l'index unique sur cette colonne.

ALTER TABLE MYTABLE 
ADD COL2 AS (CASE WHEN COL1 IS NULL THEN CAST(ID AS NVARCHAR(255)) ELSE COL1 END) 

CREATE UNIQUE INDEX UQ_COL2 ON MYTABLE (COL2) 

Ceci suppose que l'ID est le PK de votre table et que COL1 est la colonne "unique ou nulle".

La colonne calculée (COL2) utilisera la valeur PK si votre colonne "unique" est null.

Il y a encore la possibilité de collisions entre la colonne ID et COL1 dans l'exemple suivant:

ID  COL1 COL2 
1  [NULL] 1 
2  1  1 

Pour contourner cela, je crée habituellement une autre colonne calculée qui stocke si la valeur de COL2 provient de l'ID colonne ou la colonne COL1:

ALTER TABLE MYTABLE 
ADD COL3 AS (CASE WHEN COL1 IS NULL THEN 1 ELSE 0 END) 

l'indice devrait être remplacé:

CREATE UNIQUE INDEX UQ_COL2 ON MYTABLE (COL2, COL3) 

Maintenant, l'index est sur les deux colonnes calculées COL2 et col3 donc il n'y a pas de problème:

ID  COL1 COL2 COL3 
1  [NULL] 1  1 
2  1  1  0