web-dev-qa-db-ja.com

2つのNULL可能列には値が必要です

説明なしの質問:

とにかく、常に1が値を必要とする2つのnull値の制約を持つことはありますか?たとえば、2つの日付列はどちらもnullですが、値が必要な少なくとも 1です。

問題の説明:

Expenseというテーブルがあるとします

そして2つの日付があります:

prevision_expense_expiration_date DATE NULLABLEense_payment_date DATE NULLABLE

これら2つの列のロジックは次のとおりです。

私は何かを購入しましたが、電話代のような日付で支払う必要があることはわかっています。私はこれをexpense_payment_dateとともに費用として入力します。この日付は支払予定日ですが、請求書の有効期限のような実際の支払い日ではありません。

他の状況では、あるサービスのギフトカードを販売しています。私はmayクライアントに転送されたサービスをプロバイダーに購入する費用がありますonlyクライアントがカードを利用する場合。したがって、ギフトカードには有効期限があります。ギフトカードが有効な期間の費用として挿入せずに、その「費用」を予測します。ギフトカードの有効期限が切れた場合、その「費用」はアカウントに入力しないでくださいシステム。

Prevision_expenseとconfirm_expenseと呼ばれる2つの同等のテーブルを使用できることはわかっていますが、正しくないため、同じテーブルに2つの日付があり、null値を指定できますが、常に必要になるように制約または何かしたいのです。

別の可能な戦略があります:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

したがって、この場合、日付が規定値である場合、ブール値は1になります。それ以外の場合は0になります。null値はなく、すべて良好です。例外として、最初に準備日があり、THEN(2日後と言います)にその費用の確認日があるときに、両方の値を保存するオプションが必要です。この場合、戦略2では、そのオプションはありません。

私はデータベース設計ですべてを間違っていますか? :D

10
Bart Calixto

JDシュミットの回答のバージョンですが、余分な列のぎこちなさはありません。

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
10

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のcaseステートメントは、列AとBの両方がnullの場合、null値を返しますが、計算列のNot Null制約により、挿入を妨げるエラーが発生します。それ以外の場合は1を返します。

計算列は永続化されるため、テーブルに物理的に格納されます。バイナリに変換すると、この影響が最小限に抑えられ、列が長さ1のバイナリデータ型になります。

2
Shane Estelle

同じように見える記事を見つけた here

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
1
JD Schmidt