Проверьте ограничение только один из трех столбцов не является нулевым

61

У меня есть (SQL Server) таблица, которая содержит 3 типа результатов: FLOAT, NVARCHAR (30) или DATETIME (3 отдельных столбца). Я хочу убедиться, что для любой данной строки, только один столбец имеет результат, а другие столбцы имеют значение NULL. Что является самым простым проверочным ограничением для достижения этого?

Контекст для этого - попытка дооснастить способность фиксировать нечисловые результаты в существующей системе. Добавление двух новых столбцов в таблицу с ограничением для предотвращения более одного результата на строку было наиболее экономичным подходом, не обязательно правильным.

Обновление: Извините, тип данных snafu. К сожалению, я не хотел, чтобы указанные типы результатов интерпретировались как типы данных SQL Server, просто общие термины, исправленные сейчас.

Дэвид Кларк
источник

Ответы:

72

Следующее должно сделать трюк:

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

Вероятно, вам потребуется выполнить три теста в пределах ограничения: по одному тесту для каждой пары, для которой вы хотите задать значение NULL, и один для столбца, который должен быть не 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)
);
mrdenny
источник
Это не так масштабируемо, у меня есть таблица с 9 внешними ключами, и только один должен быть не нулевым, я предпочитаю решение @MarkStoreySmith
Амир Пашазаде
5

Вот решение PostgreSQL, использующее встроенные функции массива :

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
источник
Будет ли это более быстрой реализацией в postgreSQL, чем ранее упомянутые решения CASE или AND / OR, опубликованные Марком Стори и mrdenny соответственно?
Крис Бритт