两个可为空的列,其中一个必须具有值


10

不解释问题:

无论如何,是否有2个空值的约束总是需要1个有值?例如,两个日期列都为空,但至少有 1个需要具有一个值

问题描述:

假设我有一个名为Expense的表格

并有两个日期:

prevision_expense_expiration_date DATE为空ABLEABLE_payment_date DATE为空

这两列的逻辑如下:

我买了东西,我知道我得为此花些钱,例如电话费。我将其作为费用输入,并带有expense_payment_date。该日期是我应该支付的假定日期,而不是实际的支付日期,例如发票的到期日期。

在其他情况下,我会出售某些提供者提供的服务的礼品卡。我可能有买我的提供商的服务转移到我的客户的费用只有当客户赎回卡。因此,礼品卡有一个有效期,我想对该“费用”做一个预先准备,而不插入礼品卡有效期内的费用,如果礼品卡过期,则“费用”不应输入到帐户中系统。

我知道我可以有2个相等的表,分别称为prevision_expense和Confirmed_expense,但是听起来不对,所以我在同一张表中有2个日期,可以为空,但是我想约束一下,以便始终需要一个。

还有另一种可行的策略:

payment_date DATE NOT NULL is_prevision_date BOOL NOT NULL

因此,在这种情况下,如果日期为prevision,则bool值将为1,否则将为0。没有空值,一切都很好。除了我希望可以选择在第一次有预先设定日期时存储两个值,然后(然后说两天后说)确定该费用的日期时,在这种情况下,采用策略2时,我将没有该选项。

我在数据库设计中做错了什么吗?:D

Answers:


10

JD Schmidt答案的一个版本,但没有多余的专栏文章:

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和列B均为空,则计算列A_and_B中的case语句将返回空值,但是对计算列的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) )
Shane Estelle

好答案!这一个比另一个更合适,但是由于我使用的是MySQL,因此对CHECK CLAUSE进行了解析,但在MySQL上却被忽略了,因此我将另一个答案标记为可接受的答案。+1
巴里·卡利克斯托

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值。
巴里·卡利克斯托
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.