Oracle 如何在一个表中的一列和两个父表中的一列之间实施一对多关系?
我在现有的数据库应用程序中有三个表。我没有ERDs或其他关于表关系的文档。没有现有的主键或外键。 我希望对关系进行建模,并在理想情况下使用约束强制执行关系。Oracle 如何在一个表中的一列和两个父表中的一列之间实施一对多关系?,oracle,database-design,referential-integrity,Oracle,Database Design,Referential Integrity,我在现有的数据库应用程序中有三个表。我没有ERDs或其他关于表关系的文档。没有现有的主键或外键。 我希望对关系进行建模,并在理想情况下使用约束强制执行关系。 订单表可以有任意数量的零件订单。Orders.part列的值必须存在于零件库存表或零件库存表中。我认为最初的设计应该有一个带有类型列的parts表,但这就是我所拥有的。是否有方法对这些关系进行建模/实施 命令 零件库存 第四部分 不幸的是没有 您可以定义两个外键约束,但它们都需要对表中的每一行有效 第二个问题是char数据类型的选择。ORD
订单表可以有任意数量的零件订单。Orders.part列的值必须存在于零件库存表或零件库存表中。我认为最初的设计应该有一个带有类型列的parts表,但这就是我所拥有的。是否有方法对这些关系进行建模/实施 命令 零件库存 第四部分 不幸的是没有 您可以定义两个外键约束,但它们都需要对表中的每一行有效
第二个问题是
char
数据类型的选择。ORDERS中没有char(10)
值与任一父表中的char(50)
匹配。可能是选择数据类型的人对它不熟悉。仅考虑到您现有的表,您不能这样做。但这并不是说不可能做到。:-)
理想情况下,您应该将ORDER.PART
更改为CHAR(50)
,以匹配其他表上的PARTS\u NUM
字段,否则您将无法在订单上存储所有可能的PARTS\u NUM。也许这是商业原因,也许有人只是无知。没关系——如果可以的话,试着把它修好。但这不会完全阻止你
创建另一个表-我们称之为PARTS\u ALL。它有两个字段-PARTS_NUM(应与PARTS_INV和PARTS_noniv上PARTS_NUM字段的数据类型匹配)和SOURCE(应为VARCHAR2(1))。将(PARTS_NUM,SOURCE)的组合作为主键。看来
CREATE TABLE ALL_PARTS
(PARTS_NUM CHAR(50),
SOURCE VARCHAR2(1) NOT NULL,
CONSTRAINT PK_ALL_PARTS
PRIMARY KEY (PARTS_NUM, SOURCE));
然后将数据从PARTS\U INV和PARTS\U NONIV复制到所有零件中,添加适当的源值:
INSERT INTO ALL_PARTS (PARTS_NUM, SOURCE)
SELECT PARTS_NUM, 'I' AS SOURCE FROM PARTS_INV
UNION ALL
SELECT PARTS_NUM, 'I' AS SOURCE FROM PARTS_NONINV
现在,您在PARTS\U INV和PARTS\U NONIV上定义了两个触发器,以将这些表上的插入和删除传播到所有零件:
CREATE TRIGGER PARTS_INV_AID
AFTER INSERT OR DELETE ON PARTS_INV
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO ALL_PARTS (PARTS_NUM, SOURCE) VALUES (:NEW.PARTS_NUM, 'I');
ELSIF DELETING THEN
DELETE FROM ALL_PARTS WHERE PARTS_NUM = :OLD.PARTS_NUM;
END IF;
END PARTS_INV_AID;
/
CREATE TRIGGER PARTS_NONINV_AID
AFTER INSERT OR DELETE ON PARTS_NONINV
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO ALL_PARTS (PARTS_NUM, SOURCE) VALUES (:NEW.PARTS_NUM, 'N');
ELSIF DELETING THEN
DELETE FROM ALL_PARTS WHERE PARTS_NUM = :OLD.PARTS_NUM;
END IF;
END PARTS_NONINV_AID;
/
现在,您的应用程序套件可以像往常一样继续插入和删除PARTS\u INV和PARTS\u noniv中的行,这些更改将传播到所有PARTS
现在(最后!)您可以定义从订单到所有零件的外键,并获得所需的验证:
ALTER TABLE ORDER
ADD CONSTRAINT ORDER_FK1
FOREIGN KEY (PART) REFERENCES ALL_PARTS (PARTS_NUM);
这是否理想?不。理想情况下,你应该去掉PARTS\u INV和PARTS\u nonv,用所有的零件替换它们,修改你的所有应用程序、web应用程序和后台软件以使用新表-所以可能不会发生-但是考虑到你发现自己身处其中的情况,这可能是你能做的最好的事情
这里有一种实现方法,使用物化视图 在我完整的工作示例中,我创建了两个表,T1和T2。每个表都有一个ID列,另一个列在两个表中不相同(一个表中有名称,另一个表中有价格,甚至数据类型也不相同)。任务是从两个表的所有列ID的联合中创建主键 首先,ID必须是每个表中的PK。然后,我们从两个表中的所有ID列的并集创建一个物化视图(MV),并将MV中的列ID声明为MV的主键。然后,在最后一步中,我创建了一个子表T,其列ID和外键指向MV(不是T1或T2,而是来自两个表的所有ID的并集) 您可能需要测试使用三个表(T1、T2和T)可以做的各种事情,以了解这些是如何工作的 需要记住两件事:(1)表上的PK和FK约束通常在插入/更新/删除时检查(如果约束是“立即的”);对于T1和T2,ID上的PK约束将分别为。但是,仅在提交时检查MV上的jointPK约束。表T上的FK约束被选中为“立即”(在每个INSERT、UPDATE、DELETE或MERGE语句之后)。(2) 检查MV上的PK约束会给T1和T2上的DML语句增加边际开销 下面是:
create table t1 (id number primary key, name varchar2(100));
create table t2 (id number primary key, price number not null);
create materialized view log on t1 with primary key, rowid including new values;
create materialized view log on t2 with primary key, rowid including new values;
create materialized view t12 (id, rid, source)
refresh fast on commit
as
select id, rowid, 't1' from t1 union all select id, rowid, 't2' from t2
;
alter materialized view t12 add constraint t12_pk primary key (id);
create table t (id number references t12, eff_date date not null);
您还可以在
订单
表上创建插入和更新触发器
,以满足要求
根据您的评论,PART_NUM
在两个表中是唯一的。因此,您可以按如下方式创建触发器:
CREATE OR REPLACE TRIGGER ORDERS_TRG BEFORE
INSERT OR UPDATE ON ORDERS
FOR EACH ROW
DECLARE
LV_COUNT NUMBER;
BEGIN
SELECT
1
INTO LV_COUNT
FROM
DUAL
WHERE
:NEW.PART IN (
SELECT
PART_NUM
FROM
PARTS_INV
UNION ALL
SELECT
PART_NUM
FROM
PARTS_NONINV
);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE;
END ORDERS_TRG;
/
根据您的要求更新异常处理部分
干杯 这是一个常见问题。在考虑发帖之前,请始终用谷歌搜索任何错误消息,以及你的问题/问题/目标的许多清晰、简洁和准确的措辞,有或没有你的特定字符串、名称和行号,然后阅读许多答案。如果你发布一个问题,用一句话作为标题。请参阅文本上方的投票箭头鼠标(&U)。可能重复的是PARTS\u INV.PART\u NUM UNION所有PARTS\u nonv.PARTS\u NUM唯一?感谢您的回复。我正在考虑这些建议。回答问题:两个表中的零件都是唯一的。此外,parts\u inv或parts\u noniv中的零件都不超过20个字符,因此可能会更改数据类型以匹配订单。在我看来(公认为非专业人士),这是一个糟糕的建议。显然,您还需要为更新操作编写触发器代码。您必须防止在新创建的表上使用DML。对于物化视图,而不是新的表加触发器,您不会遇到这些问题。@mathguy:我最初考虑使用materialized视图,但我对它们没有太多经验,所以我不熟悉如何使用它们。是否可以在物化视图上定义主键或唯一键,然后是否可以在引用MV的另一个表上创建外键?我试着在dbfiddle.uk上简单地使用它,但发现我无法在那里制作MV,所以放弃了这个想法。我很想看到一个丰满的
ALTER TABLE ORDER
ADD CONSTRAINT ORDER_FK1
FOREIGN KEY (PART) REFERENCES ALL_PARTS (PARTS_NUM);
create table t1 (id number primary key, name varchar2(100));
create table t2 (id number primary key, price number not null);
create materialized view log on t1 with primary key, rowid including new values;
create materialized view log on t2 with primary key, rowid including new values;
create materialized view t12 (id, rid, source)
refresh fast on commit
as
select id, rowid, 't1' from t1 union all select id, rowid, 't2' from t2
;
alter materialized view t12 add constraint t12_pk primary key (id);
create table t (id number references t12, eff_date date not null);
CREATE OR REPLACE TRIGGER ORDERS_TRG BEFORE
INSERT OR UPDATE ON ORDERS
FOR EACH ROW
DECLARE
LV_COUNT NUMBER;
BEGIN
SELECT
1
INTO LV_COUNT
FROM
DUAL
WHERE
:NEW.PART IN (
SELECT
PART_NUM
FROM
PARTS_INV
UNION ALL
SELECT
PART_NUM
FROM
PARTS_NONINV
);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE;
END ORDERS_TRG;
/