Vérification de la contrainte: seule l'une des trois colonnes est non nulle

61

J'ai une table (SQL Server) qui contient 3 types de résultats: FLOAT, NVARCHAR (30) ou DATETIME (3 colonnes séparées). Je veux m'assurer que pour une ligne donnée, une seule colonne a un résultat et les autres colonnes sont NULL. Quelle est la contrainte de vérification la plus simple pour y parvenir?

Le contexte pour cela est d'essayer de moderniser la possibilité de capturer des résultats non numériques dans un système existant. L'ajout de deux nouvelles colonnes à la table avec une contrainte pour empêcher plus d'un résultat par ligne était l'approche la plus économique, pas nécessairement la bonne.

Mise à jour: Désolé, type de données snafu. Malheureusement, je ne voulais pas que les types de résultats indiqués soient interprétés comme des types de données SQL Server, juste des termes génériques, corrigés maintenant.

David Clarke
la source

Réponses:

72

Ce qui suit devrait faire l'affaire:

CREATE TABLE MyTable (col1 FLOAT NULL, col2 NVARCHAR(30) NULL, col3 DATETIME NULL);
GO

ALTER TABLE MyTable
ADD CONSTRAINT CheckOnlyOneColumnIsNull
CHECK 
(
    ( CASE WHEN col1 IS NULL THEN 0 ELSE 1 END
    + CASE WHEN col2 IS NULL THEN 0 ELSE 1 END
    + CASE WHEN col3 IS NULL THEN 0 ELSE 1 END
    ) = 1
)
GO
Mark Storey-Smith
la source
24

Vous devrez probablement effectuer trois tests dans la contrainte, un pour chaque paire que vous souhaitez définir comme null et un pour la colonne qui ne doit pas être nulle:

ALTER TABLE table
ADD CONSTRAINT CK_one_is_null
CHECK (
     (col1 IS NOT NULL AND col2 IS NULL AND col3 IS NULL)
  OR (col2 IS NOT NULL AND col1 IS NULL AND col3 IS NULL) 
  OR (col3 IS NOT NULL AND col1 IS NULL AND col2 IS NULL)
);
mrdenny
la source
Ce n'est pas si évolutif, j'ai une table avec 9 clés étrangères et une seule devrait être non null, je préfère la solution de @MarkStoreySmith
Amir Pashazadeh
5

Voici une solution PostgreSQL utilisant les fonctions de tableau intégrées :

ALTER TABLE your_table
ADD chk_only_one_is_not_null CHECK (array_length(array_remove(ARRAY[col1::text, col2::text, col3::text], NULL), 1) = 1);
CrEOF
la source
Cette implémentation dans postgreSQL sera-t-elle plus rapide que les solutions CASE ou AND / OR mentionnées précédemment publiées par Mark Storey et mrdenny, respectivement?
Chris Britt