it-swarm.it

Controlla il vincolo solo una delle tre colonne non è nulla

Ho una tabella (SQL Server) che contiene 3 tipi di risultati: FLOAT, NVARCHAR (30) o DATETIME (3 colonne separate). Voglio garantire che per ogni data riga, solo una colonna abbia un risultato e le altre colonne siano NULL. Qual è il vincolo di controllo più semplice per raggiungere questo obiettivo?

Il contesto per questo sta cercando di aggiornare la capacità di acquisire risultati non numerici in un sistema esistente. L'aggiunta di due nuove colonne alla tabella con un vincolo per impedire più di un risultato per riga è stato l'approccio più economico, non necessariamente quello corretto.

Aggiornamento: mi dispiace, tipo di dati snafu. Purtroppo non intendevo che i tipi di risultati indicati fossero interpretati come tipi di dati di SQL Server, solo termini generici, risolti ora.

65
David Clarke

Quanto segue dovrebbe fare il trucco:

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
78

Probabilmente dovrai eseguire tre test all'interno del vincolo, un test per ogni coppia che vuoi essere nullo e uno per la colonna che non dovrebbe essere null:

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)
);
24
mrdenny

Ecco una soluzione PostgreSQL che utilizza funzioni di array integrate :

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);
5
CrEOF

FOR POSTGRESQL

CHECK( (col_1 IS NOT NULL)::integer + (col_2 IS NOT NULL)::integer + ... = 1 )

Convertiamo la colonna in booleano con IS NOT NULL ( true o false ), quindi esegui il cast in :: numero intero ( 0 o 1 ) Possiamo quindi usare operatori aritmetici

= 1  //must one row is not null  
<= 1 //only one row can be not null
1
Mendes