Dua kolom yang dapat dibatalkan satu harus memiliki nilai

10

Pertanyaan Tanpa Penjelasan:

Adakah yang memiliki batasan 2 nilai null yang selalu mengharuskan 1 memiliki nilai? Misalnya dua kolom tanggal keduanya nol tetapi memiliki setidaknya 1 yang mengharuskan memiliki nilai

Deskripsi Masalah:

Katakanlah saya punya meja bernama Beban

dan memiliki 2 tanggal:

prevision_expense_expiration_date DATE NULLABLE cost_payment_date DATE NULLABLE

logika 2 kolom tersebut adalah sebagai berikut:

Saya melakukan pembelian sesuatu dan saya tahu saya harus membayarnya, tanggal tertentu, seperti tagihan telepon. Saya akan memasukkan ini sebagai pengeluaran dengan cost_payment_date. Tanggal ini adalah tanggal yang seharusnya saya bayar tetapi bukan tanggal pembayaran yang sebenarnya, seperti tanggal kadaluwarsa faktur.

Dalam situasi lain saya menjual kartu hadiah dari beberapa penyedia untuk layanan itu. Saya mungkin memiliki biaya untuk membeli ke penyedia saya layanan yang ditransfer ke klien saya hanya jika klien menebus kartu. Karenanya kartu hadiah memiliki tanggal kedaluwarsa, saya ingin melakukan previsi untuk 'pengeluaran' itu tanpa memasukkan sebagai biaya untuk waktu kartu hadiah valid, jika kartu hadiah kedaluwarsa, 'biaya' itu tidak boleh dimasukkan ke dalam akun sistem.

Saya tahu saya dapat memiliki 2 tabel yang sama yang disebut prevision_expense dan confirm_expense tetapi tidak terdengar benar sehingga saya ada di tabel yang sama, 2 tanggal, nullable, tetapi saya ingin membatasi atau sesuatu sehingga selalu diperlukan.

Ada strategi lain yang mungkin:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

Jadi, dalam hal ini, jika tanggal adalah nilai bool prevision akan menjadi 1, jika tidak akan menjadi 0. Tidak ada nilai null, semuanya baik kecuali bahwa saya ingin opsi untuk menyimpan KEDUA nilai ketika pertama kali saya memiliki tanggal previsi dan MAKA (katakanlah dua hari kemudian) memiliki tanggal yang dikonfirmasi untuk pengeluaran itu, dalam hal ini dengan strategi 2 saya tidak akan memiliki opsi itu.

Apakah saya melakukan semua yang salah dalam desain database? : D

Bart Calixto
sumber

Jawaban:

10

Versi jawaban JD Schmidt, tetapi tanpa kejanggalan kolom tambahan:

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

Jika Anda menggunakan SQL Server, Anda bisa menghindari penggunaan pemicu dengan menggunakan kolom yang tetap dihitung di tabel Anda:

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

Pernyataan kasus dalam kolom yang dihitung A_and_B akan mengembalikan nilai nol jika kedua kolom A dan B adalah nol, tetapi batasan Not Null pada kolom yang dikomputasi kemudian akan meningkatkan kesalahan mencegah penyisipan. Kalau tidak mengembalikan 1.

Karena kolom yang dihitung tetap ada, maka secara fisik akan disimpan dalam tabel. Konversi ke biner meminimalkan dampak ini, menjadikan kolom tipe data biner dengan panjang 1.

Shane Estelle
sumber
1
Di SQL-Server, Anda bisa melakukan ini dengan CHECKkendala juga. Tidak perlu untuk kolom yang bertahan.
ypercubeᵀᴹ
1
Luar biasa, itu memang terlihat sedikit lebih bersih. 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
jawaban bagus! Yang ini lebih tepat daripada yang lain tetapi karena saya menggunakan MySQL, CHECK CLAUSE diuraikan tetapi diabaikan pada MySQL jadi saya menandai jawaban yang lain sebagai yang diterima. +1
Bart Calixto
1

Saya menemukan sebuah artikel yang terlihat seperti hal yang sama di sini

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
sumber
terima kasih, saya menggunakan MySQL dan ini berfungsi dengan baik dengannya. khususnya karena melemparkan nilai nol pada bukan kesalahan kolom nol.
Bart Calixto