繁体   English   中英

如何提供数据库约束以确保表中的此行为?

[英]How to give a database constraint to ensure this behavior in a table?

我有一个有五列的表:A,B,C,D和E.

我需要遵守以下限制:

  • A是主键。
  • 对于B,只能有一个C,即:1-1; 2-1; 3-2但不是1-2。
  • BC和D可以取任何值,但不能重复,即:1-1 1; 1-1 2; 再没有1-1 1。
  • E可以取任何价值。

所以,考虑以下顺序

| A | B | C | D | E |

| 1 | 1 | 1 | 1 | 1 | -> OK 

| 2 | 1 | 2 | 1 | 1 | -> Should fail, because there is a B with another C, 1-2 must be 1-1.

| 3 | 1 | 1 | 2 | 1 | -> OK

| 4 | 1 | 1 | 2 | 1 | -> Should fail, because relation between B-C and D is repeated.

| 5 | 2 | 1 | 1 | 1 | -> OK

有没有办法通过数据库中的某些约束来遵守这种行为?

谢谢!

对于B - C规则,我会创建一个触发器

对于B - C - D规则看起来你想要唯一的约束

ALTER TABLE t ADD CONSTRAINT uni_BCD UNIQUE (B,C,D);

A和E与问题无关,可以忽略。

通过在BCD上创建唯一索引,可以轻松解决BCD规则。

如果对于每个B只有一个C,那么您的DB不会被标准化。 使用B和C创建一个新表。使B成为主键或在B上创建唯一索引。然后从原始表中删除C. (此时BCD上的唯一索引成为BD上的唯一索引。)

如果不对表进行规范化,我认为没有任何方法可以使用约束来实现。 你当然可以使用触发器或代码来完成它。

这种情况并非无足轻重

对于B,只能有一个C,即:1-1; 2-1; 3-2但不是1-2。

,因为Oracle不支持CREATE ASSERTION (很快,我们希望!)

因此,您需要涉及第二个表来强制执行此约束,否则需要使用语句级别的AFTER INSERT / UPDATE触发器。

我要做的是创建第二个表并通过视图上的INSTEAD OF触发器进行维护,并确保通过视图发生我的所有应用程序DML。 (你也可以在桌子上创建一个常规触发器并让它维护第二个表。这不是我的偏好。我发现INSTEAD OF触发器更灵活,更可见。)

如果不清楚,第二个表的目的是它允许您将约束强制执行为FOREIGN KEY约束。 第二个表上的UNIQUEPRIMARY KEY约束确保B每个值仅出现一次。

以下是该方法的示例代码:

--DROP TABLE table1_parent;
--DROP TABLE table1;


CREATE TABLE table1_parent
 ( b number NOT NULL,
   c number NOT NULL,
   constraint table1_parent_pk PRIMARY KEY (b),
   constraint table1_parent_u1 UNIQUE (b, c) );

CREATE TABLE table1
(
  a   NUMBER NOT NULL,
  b   NUMBER NOT NULL,
  c   NUMBER NOT NULL,
  d   NUMBER NOT NULL,
  e   NUMBER NOT NULL,
  CONSTRAINT table1_pk PRIMARY KEY (a),  -- "A is the primary key."
  CONSTRAINT table1_fk FOREIGN KEY ( b, c ) REFERENCES table1_parent ( b, c ), -- "For a B there can only be one C, ie: 1-1 ; 2-1 ; 3-2 but not 1-2."
  CONSTRAINT table1_u2 UNIQUE ( b, c, d ) -- "B-C and D can take any value bue can not be repeated, ie: 1-1 1 ; 1-1 2 ; not 1-1 1 again."
);

CREATE INDEX table1_n1 ON table1 (b,c); -- Always index foreign keys

CREATE OR REPLACE VIEW table1_dml_v AS SELECT * FROM table1;

CREATE OR REPLACE TRIGGER table1_dml_v_trg INSTEAD OF INSERT OR UPDATE OR DELETE ON table1_dml_v
DECLARE
  l_cnt NUMBER;
BEGIN
  IF INSERTING THEN
    BEGIN
      INSERT INTO table1_parent (b, c) VALUES ( :new.b, :new.c );
    EXCEPTION
      WHEN dup_val_on_index THEN
        NULL;  -- parent already exists, no problem
    END;

    INSERT INTO table1 ( a, b, c, d, e ) VALUES ( :new.a, :new.b, :new.c, :new.d, :new.e );
  END IF;

  IF DELETING THEN
    DELETE FROM table1 WHERE a = :old.a;

    SELECT COUNT(*) INTO l_cnt FROM table1 WHERE b = :old.b AND c = :old.c;

    IF l_cnt = 0 THEN
      DELETE FROM table1_parent WHERE b = :old.b AND c = :old.c;
    END IF;
  END IF;

  IF UPDATING THEN
    BEGIN
      INSERT INTO table1_parent (b, c) VALUES ( :new.b, :new.c );
    EXCEPTION
      WHEN dup_val_on_index THEN
        NULL;  -- parent already exists, no problem
    END;

    UPDATE table1 SET a = :new.a, b = :new.b, c = :new.c, d = :new.d, e = :new.d WHERE a = :old.a;

    SELECT COUNT(*) INTO l_cnt FROM table1 WHERE b = :old.b AND c = :old.c;

    IF l_cnt = 0 THEN
      DELETE FROM table1_parent WHERE b = :old.b AND c = :old.c;
    END IF;

  END IF;      
END;


insert into table1_dml_v ( a,b,c,d,e) VALUES (1,1,1,1,1);
insert into table1_dml_v ( a,b,c,d,e) VALUES (2,1,2,1,1);
insert into table1_dml_v ( a,b,c,d,e) VALUES (3,1,1,2,1);
insert into table1_dml_v ( a,b,c,d,e) VALUES (4,1,1,2,1);
insert into table1_dml_v ( a,b,c,d,e) VALUES (5,2,1,1,1);

如果您的系统支持快速刷新的物化视图,请尝试以下操作。
由于我目前无法访问此功能,因此无法验证解决方案。

create materialized view log on t with primary key;

create materialized view t_mv
refresh fast
as
select      b,c
from        t
group by    b,c
;

alter table t_mv add constraint t_mv_uq_b unique (b);

当然:

alter table t add constraint t_uq_a_b_c unique (b,c,d);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM