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.

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

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

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

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.