Dwie zerowalne kolumny, z których jedna musi mieć wartość

10

Pytanie bez wyjaśnienia:

Czy w ogóle istnieje ograniczenie 2 wartości zerowych, które zawsze wymaga wartości 1? Na przykład dwie kolumny daty, obie o wartości NULL, ale mające co najmniej 1, która wymaga wartości

Opis problemu:

Powiedzmy, że mam tabelę o nazwie Koszt

i mają 2 daty:

prevision_expense_expiration_date DATE NULLABLE data_płacenia_wydatków DATE NULLABLE

logika tych 2 kolumn jest następująca:

Dokonałem zakupu czegoś i wiem, że muszę za to zapłacić, pewnego dnia, jak rachunek telefoniczny. Wprowadzę to jako wydatek z datą_płatności. Ta data jest przypuszczalną datą, którą powinienem zapłacić, ale nie faktyczną datą płatności, jak data ważności faktury.

W innej sytuacji sprzedaję kartę podarunkową jakiegoś dostawcy za jej obsługę. I może mieć koszt zakupu do mojego dostawcy usługi przeniesiony do mojego klienta tylko wtedy, gdy klient wykupi kartę. W związku z tym data ważności karty upominkowej, chcę zrobić podgląd tego „wydatku” bez wstawiania jako wydatku na czas ważności karty upominkowej, jeśli wygasa karta upominkowa, „wydatek” nie powinien wejść na konto system.

Wiem, że mogę mieć 2 równe tabele o nazwie prevision_expense i potwierdź_expense, ale to nie brzmi dobrze, więc mam w tej samej tabeli, 2 daty, zerowalne, ale chcę ograniczyć lub coś takiego, aby zawsze była wymagana.

Istnieje inna możliwa strategia:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

Zatem w tym przypadku, jeśli data jest przewidywana, wartość bool wynosiłaby 1, w przeciwnym razie wynosiłaby 0. Brak wartości zerowych, wszystko jest dobrze. z wyjątkiem tego, że chcę opcję przechowywania OBU wartości, gdy najpierw mam datę prognozy, a NASTĘPNIE (powiedzmy dwa dni później) mam potwierdzoną datę tego wydatku, w takim przypadku ze strategią 2 nie mam tej opcji.

Czy robię wszystko źle podczas projektowania bazy danych? :RE

Bart Calixto
źródło

Odpowiedzi:

10

Wersja odpowiedzi JD Schmidta, ale bez niewygodności dodatkowej kolumny:

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
Michael Platings
źródło
2

Jeśli używasz programu SQL Server, możesz uniknąć użycia wyzwalacza, używając utrwalonej kolumny obliczeniowej w tabeli:

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

Instrukcja case w obliczonej kolumnie A_ i_B zwróci wartość zerową, jeśli zarówno kolumna A, jak i B są puste, ale ograniczenie Not Null w obliczonej kolumnie spowodowałoby wówczas błąd uniemożliwiający wstawienie. W przeciwnym razie zwraca 1.

Ponieważ kolumna obliczana jest utrwalona, ​​zostanie fizycznie zapisana w tabeli. Konwersja na binarną minimalizuje wpływ tego zjawiska, czyniąc kolumnę binarnym typem danych o długości 1.

Shane Estelle
źródło
1
W SQL-Server można to również zrobić z CHECKograniczeniem. Nie ma potrzeby utrzymywania kolumny.
ypercubeᵀᴹ
1
Wspaniale, to wydaje się trochę czystsze. 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) )
Shane Estelle
świetna odpowiedź! Ten jest bardziej odpowiedni niż drugi, ale ponieważ używam MySQL, SPRAWDŹ KLAUZĘ jest analizowana, ale na MySQL jest ignorowana, więc zaznaczam drugą odpowiedź jako zaakceptowaną. +1
Bart Calixto
1

Znalazłem artykuł, który wygląda tak samo tutaj

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
źródło
dzięki, używam MySQL i to działa doskonale z nim. szczególnie z powodu wyrzucenia wartości null na błąd niepustej kolumny.
Bart Calixto