Due colonne nullable una richiesta per avere valore


10

Domanda senza spiegazione:

Esiste comunque un vincolo di 2 valori null che richiede sempre 1 per avere valore? Ad esempio due colonne di date entrambe nulle ma con almeno 1 che richiede di avere un valore

Descrizione del problema:

Diciamo che ho una tabella chiamata Expense

e hanno 2 date:

prevision_expense_expiration_date DATA NULLABLE spese_payment_date DATA NULLABLE

la logica di quelle 2 colonne è la seguente:

Ho fatto un acquisto di qualcosa e so che devo pagarlo, un appuntamento, come una bolletta del telefono. Inserirò questo come una spesa con una spesa_pagata_data. Questa data è la data presunta che dovrei pagare ma non la data effettiva del pagamento, come la data di scadenza della fattura.

In altre situazioni vendo una carta regalo di alcuni provider per il suo servizio. Io possa avere le spese di acquisto al mio fornitore del servizio trasferito al mio cliente solo se il cliente riscattare la carta. Quindi la carta regalo ha una data di scadenza, voglio fare una previsione per quella "spesa" senza inserirla come spesa per il tempo in cui la carta regalo è valida, se la carta regalo scade, tale "spesa" non dovrebbe entrare nel conto sistema.

So di poter avere 2 tabelle uguali chiamate prevision_expense e confirm_expense ma non suona bene, quindi ho nella stessa tabella, 2 date, nullable, ma voglio vincolare o qualcosa in modo che uno sia sempre richiesto.

C'è un'altra strategia possibile:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

Quindi, in questo caso, se la data è la previsione il valore bool sarebbe 1, altrimenti sarà 0. Nessun valore null, tutto va bene. a parte il fatto che desidero l'opzione di memorizzare ENTRAMBI i valori quando prima ho una data di previsione e POI (diciamo due giorni dopo) hanno una data confermata per quella spesa, nel qual caso con la strategia 2 non avrò quell'opzione.

Sto sbagliando tutto nella progettazione del database? : D

Risposte:


10

Una versione della risposta di JD Schmidt, ma senza l'imbarazzo di una colonna in più:

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

Se si utilizza SQL Server, è possibile evitare l'uso di un trigger utilizzando una colonna calcolata persistente nella tabella:

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

L'istruzione case nella colonna calcolata A_and_B restituirà un valore null se entrambe le colonne A e B sono null, ma il vincolo Not Null sulla colonna calcolata genererebbe quindi un errore che impedisce l'inserimento. Altrimenti restituisce un 1.

Poiché la colonna calcolata è persistente, verrà archiviata fisicamente nella tabella. La conversione in binario riduce al minimo l'impatto di ciò, rendendo la colonna un tipo di dati binario di lunghezza 1.


1
In SQL Server, puoi farlo anche con un CHECKvincolo. Non è necessaria una colonna persistente.
ypercubeᵀᴹ

1
Fantastico, sembra un po 'più pulito. 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,

Bella risposta! Questo è più appropriato dell'altro, ma dal momento che sto usando MySQL, CHECK CLAUSE viene analizzato ma ignorato su MySQL, quindi segnare l'altra risposta come accettata. +1
Bart Calixto,

1

Ho trovato un articolo che sembra la stessa cosa qui

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

grazie, sto usando MySQL e questo funziona perfettamente con esso. specialmente a causa del lancio di un valore nullo sull'errore di colonna non nullo.
Bart Calixto,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.