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


10

Питання без пояснень:

Чи є в будь-якому випадку обмеження з 2 нульових значень, яке завжди вимагає значення 1? Наприклад, два стовпці дати є нульовими, але мають принаймні 1, що вимагає значення

Опис проблеми:

Скажімо, у мене є таблиця під назвою "Витрати"

і мають 2 дати:

prevision_expense_expiration_date DATE NULLABLE cost_payment_date DATE NULLABLE

логіка цих 2 стовпців така:

Я щось придбав і знаю, що за це я повинен заплатити, за якусь дату, як за телефонний рахунок. Я введу це як витрату із коштом_платіж_датою. Ця дата є передбачуваною датою, яку я повинен заплатити, але не фактичною датою платежу, як дата закінчення терміну дії рахунку.

В іншій ситуації я продаю подарункову карту якогось провайдера за її послугу. Можливо , у мене виникне витрата на придбання у мого провайдера послуги, передану моєму клієнту, лише якщо клієнт викупить картку. Отже, подарункова картка має термін придатності, я хочу здійснити попередній платіж за цей "рахунок", не вставляючи як витрату на час дії подарункової картки, якщо термін дії подарункової картки закінчився, цей "рахунок" не повинен надходити на рахунок система.

Я знаю, що у мене можуть бути дві однакові таблиці, які називаються prevision_expense та підтверджені_expense, але це не звучить правильно, тому я маю в тій самій таблиці, 2 дати, незмінні, але я хочу обмежувати чи щось так, що завжди потрібна одна.

Є ще одна можлива стратегія:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

Так, у цьому випадку, якщо дата є попередньою, значення bool було б 1, інакше буде 0. Немає нульових значень, все добре. за винятком того, що я хочу можливість зберігати значення BOTH, коли спочатку у мене є дата попереднього перегляду, а ТОГО (дозволяє сказати два дні пізніше) мати підтверджену дату цього видатку, і в такому випадку для стратегії 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 
);

Оператор регістру в обчислюваному стовпчику 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

дякую, я використовую MySQL, і це прекрасно справляється з ним. спеціально через викидання нульового значення на помилку стовпця, що не має значення.
Барт Калікто
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.