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


10

説明なしの質問:

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

問題の説明:

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

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

prevision_expense_expiration_date DATE NULLABLEense_payment_date DATE NULLABLE

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

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

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

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

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

2

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のバイナリデータ型になります。


1
SQL-Serverでは、CHECK制約を使用してこれを行うこともできます。永続化された列は必要ありません。
ypercubeᵀᴹ

1
すごい、それは少しきれいに見えます。 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) )
シェーンエステル2013年

すばらしい答えです!これは他よりも適切ですが、MySQLを使用しているので、CHECK CLAUSEは解析されますがMySQLでは無視されるため、他の回答を承認済みとしてマークします。+1
Bart Calixto 2013年

1

ここで同じような記事を見つけました

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

おかげで、私はMySQLを使用していて、これはそれで完璧に動作します。特に、nullではない列のエラーでnull値をスローするためです。
Bart Calixto 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.