Restrição de verificação: apenas uma das três colunas é nula

61

Eu tenho uma tabela (SQL Server) que contém 3 tipos de resultados: FLOAT, NVARCHAR (30) ou DATETIME (3 colunas separadas). Quero garantir que, para qualquer linha, apenas uma coluna tenha um resultado e as outras sejam NULL. Qual é a restrição de verificação mais simples para conseguir isso?

O contexto para isso é tentar atualizar a capacidade de capturar resultados não numéricos em um sistema existente. Adicionar duas novas colunas à tabela com uma restrição para impedir mais de um resultado por linha foi a abordagem mais econômica, não necessariamente a correta.

Atualização: Desculpe, tipo de dados snafu. Infelizmente, não pretendia que os tipos de resultados indicados fossem interpretados como tipos de dados do SQL Server, apenas termos genéricos, corrigidos agora.

David Clarke
fonte

Respostas:

72

O seguinte deve fazer o truque:

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
fonte
24

Você provavelmente precisará fazer três testes dentro da restrição, um teste para cada par que você deseja que seja nulo e outro para a coluna que não deve ser nula:

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
fonte
Isso não é tão escalável, eu tenho uma tabela com 9 chaves estrangeiras e apenas uma não deve ser nula. Prefiro a solução do
@MarkStoreySmith
5

Aqui está uma solução PostgreSQL usando as funções de matriz incorporadas :

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
fonte
Essa implementação será mais rápida no postgreSQL do que as soluções CASE ou AND / OR mencionadas anteriormente, postadas por Mark Storey e mrdenny, respectivamente?
22818 Chris Britt