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

10

Вопрос без объяснений:

Есть ли в любом случае ограничение 2 пустых значений, для которых всегда требуется 1, чтобы иметь значение? Например, два столбца даты, оба равны нулю, но имеют как минимум 1, для которого требуется значение

Описание проблемы:

Допустим, у меня есть таблица под названием Расход

и иметь 2 даты:

prevision_expense_expiration_date DATE NULLABLE расход_плата_date DATE NULLABLE

логика этих двух столбцов следующая:

Я сделал покупку чего-то, и я знаю, что должен заплатить за это, некоторая дата, как счет за телефон. Я введу это как расход с расходом_платы_даты. Эта дата является предполагаемой датой, которую я должен заплатить, но не фактической датой платежа, как дата истечения срока счета.

В другой ситуации я продаю подарочную карту какого-либо провайдера для его обслуживания. У меня могут быть расходы на покупку моему провайдеру услуг, переданных моему клиенту, только если клиент выкупает карту. Следовательно, у подарочной карты есть срок годности, я хочу сделать предвидение для этих «расходов», не вставляя в качестве расхода на время действия подарочной карты, если срок действия подарочной карты истекает, эти «расходы» не должны входить в счет система.

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

Есть еще одна возможная стратегия:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

Таким образом, в этом случае, если дата предвидения, значение bool будет 1, в противном случае будет 0. Нет нулевых значений, все хорошо. за исключением того, что я хочу сохранить оба значения, когда у меня сначала есть дата предвидения, а у THEN (скажем, через два дня) есть подтвержденная дата для этих расходов, и в случае со стратегией 2 у меня такого варианта не будет.

Я делаю все неправильно в дизайне базы данных? : D

Барт Каликсто
источник

Ответы:

10

Версия ответа Д.Д. Шмидта, но без неловкости дополнительного столбца:

CREATE TABLE foo (
  FieldA INT,
  FieldB INT
);

DELIMITER //
CREATE TRIGGER InsertFieldABNotNull BEFORE INSERT ON foo
FOR EACH ROW BEGIN
  IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = '\'FieldA\' and \'FieldB\' cannot both be null';
  END IF;
END//
CREATE TRIGGER UpdateFieldABNotNull BEFORE UPDATE ON foo
FOR EACH ROW BEGIN
  IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN
    SIGNAL SQLSTATE '45000'
    SET MESSAGE_TEXT = '\'FieldA\' and \'FieldB\' cannot both be null';
  END IF;
END//
DELIMITER ;

INSERT INTO foo (FieldA, FieldB) VALUES (NULL, 10); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (10, NULL); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, NULL); -- gives error
UPDATE foo SET FieldA = NULL; -- gives error
Майкл Плакингс
источник
2

Если вы используете SQL Server, вы можете избежать использования триггера, используя постоянный вычисляемый столбец в вашей таблице:

CREATE TABLE Test_Constraint
(
    A DateTime Null,
    B DateTime Null,
    A_and_B AS (CASE WHEN A IS Null AND B IS Null THEN Null ELSE Convert(Binary(1), 1) END) PERSISTED Not Null 
);

Оператор case в вычисляемом столбце A_and_B вернет нулевое значение, если оба столбца A и B имеют нулевое значение, но ограничение Not Null для вычисляемого столбца вызовет ошибку, предотвращающую вставку. В противном случае возвращается 1.

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

Шейн Эстель
источник
1
В SQL-сервере вы можете сделать это и с CHECKограничением. Нет необходимости в постоянном столбце.
ypercubeᵀᴹ
1
Круто, это кажется немного чище. CREATE TABLE Test_Constraint2 ( A DateTime Null, B DateTime Null, CONSTRAINT A_or_B_Not_Null CHECK (CASE WHEN A IS Null AND B IS Null THEN 0 ELSE 1 END = 1) )
Шейн Эстель
отличный ответ! Это более подходящее решение, чем другое, но поскольку я использую MySQL, CHECK CLAUSE анализируется, но игнорируется в MySQL, поэтому я отмечаю другой ответ как принятый. +1
Барт Каликсто
1

Я нашел статью, которая выглядит как то же самое здесь

CREATE TABLE foo (
  FieldA INT,
  FieldB INT,
  FieldA_or_FieldB TINYINT NOT NULL;
);

DELIMITER //
CREATE TRIGGER FieldABNotNull BEFORE INSERT ON foo
FOR EACH ROW BEGIN
  IF (NEW.FieldA IS NULL AND NEW.FieldB IS NULL) THEN
    SET NEW.FieldA_or_FieldB = NULL;
  ELSE
    SET NEW.FieldA_or_FieldB = 1;
  END IF;
END//
DELIMITER ;

INSERT INTO foo (FieldA, FieldB) VALUES (NULL, 10); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (10, NULL); -- OK
INSERT INTO foo (FieldA, FieldB) VALUES (NULL, NULL); -- gives error
JD Schmidt
источник
спасибо, я использую MySQL, и это прекрасно работает с ним. особенно из-за того, что выбрасывает нулевое значение на ошибку не нулевого столбца.
Барт Каликсто