我有一个包含五列的表:A,B,C,D和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
有没有办法通过数据库中的某些约束来遵守这种行为?
谢谢!
答案 0 :(得分:1)
对于B - C
规则,我会创建一个触发器
对于B - C - D
规则,您希望获得唯一约束
ALTER TABLE t ADD CONSTRAINT uni_BCD UNIQUE (B,C,D);
答案 1 :(得分:1)
A和E与问题无关,可以忽略。
通过在BCD上创建唯一索引,可以轻松解决BCD规则。
如果每个B只能有一个C,那么您的数据库不会被标准化。使用B和C创建一个新表。使B成为主键或在B上创建唯一索引。然后从原始表中删除C. (此时BCD上的唯一索引成为BD上的唯一索引。)
如果不对表进行规范化,我认为没有任何方法可以使用约束来实现。你当然可以使用触发器或代码来完成它。
答案 2 :(得分:0)
这种情况并非无足轻重
对于B,只能有一个C,即:1-1; 2-1; 3-2但不是1-2。
,因为Oracle不支持CREATE ASSERTION
(很快,我们希望!)
因此,您需要涉及第二个表来强制执行此约束,否则需要使用语句级别的AFTER INSERT / UPDATE触发器。
我要做的是创建第二个表并通过视图上的INSTEAD OF
触发器进行维护,并确保通过视图发生我的所有应用程序DML。 (你也可以在桌面上创建一个常规触发器并让 it 维护第二个表。这不是我的偏好。我发现INSTEAD OF
触发器更灵活,更可见。)
如果不清楚,第二个表的目的是允许您将约束强制为FOREIGN KEY
约束。第二个表格中的UNIQUE
或PRIMARY 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);
答案 3 :(得分:0)
如果您的系统支持快速刷新的物化视图,请尝试以下操作 由于我目前无法访问此功能,因此无法验证解决方案。
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);