Zwei nullfähige Spalten, von denen eine einen Wert haben muss

10

Keine Erklärung Frage:

Gibt es überhaupt eine Beschränkung von 2 Nullwerten, für die immer 1 einen Wert haben muss? Zum Beispiel zwei Datumsspalten, beide null, aber mindestens 1, für die ein Wert erforderlich ist

Problembeschreibung:

Angenommen, ich habe eine Tabelle namens Expense

und habe 2 Termine:

prevision_expense_expiration_date DATE NULLABLE cost_payment_date DATE NULLABLE

Die Logik dieser beiden Spalten lautet wie folgt:

Ich habe etwas gekauft und weiß, dass ich es irgendwann bezahlen muss, wie eine Telefonrechnung. Ich werde dies als Aufwand mit einem Spesenzahlungsdatum eingeben. Dieses Datum ist das voraussichtliche Datum, an dem ich bezahlen soll, aber nicht das tatsächliche Datum der Zahlung, wie das Ablaufdatum der Rechnung.

In einer anderen Situation verkaufe ich eine Geschenkkarte eines Anbieters für deren Service. Ich kann die Kosten für den Kauf des an meinen Kunden übertragenen Dienstes nur dann an meinen Anbieter tragen, wenn der Kunde die Karte einlöst. Daher hat die Geschenkkarte ein Ablaufdatum. Ich möchte eine Voraussage für diese "Ausgabe" machen, ohne sie als Ausgabe für die Zeit einzufügen, in der die Geschenkkarte gültig ist. Wenn die Geschenkkarte abläuft, sollte diese "Ausgabe" nicht in das Konto eingehen System.

Ich weiß, dass ich 2 gleiche Tabellen mit den Namen prevision_expense und verify_expense haben kann, aber es klingt nicht richtig, also habe ich in derselben Tabelle 2 Daten, nullbar, aber ich möchte einschränken oder so, so dass immer eine erforderlich ist.

Es gibt noch eine andere mögliche Strategie:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

In diesem Fall wäre der Bool-Wert 1, wenn das Datum Vorrang ist, andernfalls 0. Keine Nullwerte, alles ist gut. mit der Ausnahme, dass ich die Option möchte, BEIDE Werte zu speichern, wenn ich zuerst ein Voraussetzungsdatum habe und DANN (sagen wir zwei Tage später) ein bestätigtes Datum für diese Ausgabe hat. In diesem Fall habe ich mit Strategie 2 diese Option nicht.

Mache ich beim Datenbankdesign alles falsch? : D.

Bart Calixto
quelle

Antworten:

10

Eine Version von JD Schmidts Antwort, aber ohne die Unbeholfenheit einer zusätzlichen Spalte:

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
quelle
2

Wenn Sie SQL Server verwenden, können Sie die Verwendung eines Triggers vermeiden, indem Sie eine persistierte berechnete Spalte in Ihrer Tabelle verwenden:

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

Die case-Anweisung in der berechneten Spalte A_und_B gibt einen Nullwert zurück, wenn sowohl Spalte A als auch Spalte Null sind. Die Nicht-Null-Einschränkung für die berechnete Spalte würde dann einen Fehler auslösen, der das Einfügen verhindert. Andernfalls wird eine 1 zurückgegeben.

Da die berechnete Spalte beibehalten wird, wird sie physisch in der Tabelle gespeichert. Die Konvertierung in Binärdaten minimiert die Auswirkungen und macht die Spalte zu einem Binärdatentyp der Länge 1.

Shane Estelle
quelle
1
In SQL-Server können Sie dies auch mit einer CHECKEinschränkung tun . Keine dauerhafte Spalte erforderlich.
Ypercubeᵀᴹ
1
Genial, das scheint ein bisschen sauberer zu sein. 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
gute Antwort! Dieser ist geeigneter als der andere, aber da ich MySQL verwende, wird CHECK CLAUSE analysiert, aber in MySQL ignoriert, sodass ich die andere Antwort als die akzeptierte markiere. +1
Bart Calixto
1

Ich fand einen Artikel , dass Aussehen , die gleiche Sache wie hier

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
quelle
Danke, ich benutze MySQL und das funktioniert perfekt damit. speziell, weil bei einem Null-Spaltenfehler ein Nullwert ausgegeben wird.
Bart Calixto