Make NOT NULL约束仅适用于新行

时间:2014-08-01 05:38:52

标签: sql oracle constraints

我将尝试将这种情况作为一个最小的例子:

假设我们有一张门票表,定义如下:

CREATE TABLE ticket_id_list (
   ticket_id  NUMBER,
   issued_to  VARCHAR2(100),
   CONSTRAINT ticket_id_list_pk PRIMARY KEY (ticket_id)
);

系统已使用一段时间,数据已添加到表格中:

INSERT INTO ticket_id_list (ticket_id, issued_to)
   VALUES (1, 'Arthur');
INSERT INTO ticket_id_list (ticket_id, issued_to)
   VALUES (2, 'Ford');

稍后,弹出以下要求:

  

此时我们需要在票证表中存储一个参考号给其他东西。引用必须为 非null 。但旧记录必须具有NULL值。

(好像这听起来很愚蠢,这是一个真正的要求。)

现在,如果我们这样做:

ALTER TABLE ticket_id_list
   ADD ref_no VARCHAR2(6) NOT NULL;

约束将立即被违反,我们将获得:ORA-01758: table must be empty to add mandatory (NOT NULL) column

我们当然可以在业务逻辑中添加一个检查,但这很麻烦。由于无法解释的原因,我们无法使用默认值。有没有办法 添加仅适用于新记录的NOT NULL约束?

4 个答案:

答案 0 :(得分:7)

您可以添加检查约束,其中某个日期之后的值不能为空;类似的东西:

alter table ticket_id_list
  add constraint nul_ref
  check (ticket_id < 123456 or ref is not null);

这确实假设ticket_id只会随着时间的推移而上升,如果您的表中碰巧有一个日期字段,那么使用它可能更清楚。

答案 1 :(得分:3)

我找到了一个巧妙的伎俩:NOVALIDATE

添加一个可以为空的列:

ALTER TABLE ticket_id_list
   ADD ref_no VARCHAR2(6);

然后添加表格约束,但请务必指定NOVALIDATE

ALTER TABLE ticket_id_list
   ADD CONSTRAINT new_ref_nonull CHECK(ref_no IS NOT NULL) NOVALIDATE;

现在让我们检查一下:

INSERT INTO ticket_id_list (ticket_id, issued_to, ref_no)
   VALUES (3, 'Zaphod', '2A4252');
1 row inserted

INSERT INTO ticket_id_list (ticket_id, issued_to, ref_no)
   VALUES (4, 'Marvin', NULL);
ORA-02290: check constraint (NEW_REF_NONULL) violated

正如预期的那样。

编辑 NOVALIDATE仅在您不需要更新旧行时才有效。如果更新了旧行,则约束将失败。但是对于这个例子,这不是问题。

答案 2 :(得分:0)

如果要将约束设置为UNIQUE,那么它将允许所有先前的值可以为空,但所有新值都应该是唯一的,其默认值应为Null。

但是如果要将表列设为NOT NULL,则它应该包含默认值,或者应该隐式指定任何值。

答案 3 :(得分:0)

您必须将此要求视为业务逻辑,因为在您的情况下它是。

然后在触发器中实现此业务逻辑。

CREATE TRIGGER t_ticket_id_list
    BEFORE INSERT ON ticket_id_list
    FOR EACH ROW
      BEGIN
         IF (:NEW is null) THEN
            RAISE_APPLICATION_ERROR(-20001, 'Cannot Insert 1 in this table');
         END IF;
      END;

注意 - 同样可以为SQL Server MSDN Create trigger documentations

做同样的事情