与多个表中的一个完全相关的1对1关系

时间:2011-04-13 11:49:18

标签: sql oracle relational-database

好的,我希望我能说清楚我的问题是什么: 我有一个包含5个表的数据库。我们称它们为A和B,V_1,V_2和V_3。 A和B代表要完成的事情列表。 V_i表中描述了这些操作。现在,A代表了一种必须用某种类型的项目完成的东西的模板。另一方面,B描述了如果A描述的抽象项目必须用具体实例完成(或已经完成)。所以在OOP术语中可以说A代表一个类而B代表一个A的实例。 。无论何时将某些内容插入表B,都会复制表A中的相关数据,以便可以针对该特定项修改该数据而不会影响A.

好的,这是实际问题:如何正确建模?我主要担心的是V_i中的每条记录都不能与A和B都链接。它必须是与EITHER A或B的1对1关系。另外,V_i和V_j不能链接到A中的相同记录或B.我不知道如何正确地做到这一点。目前的结构如下:

A和B有一个叫做ID的PK。每个V_i还有一个名为ID的PK和两个引用A或B的FK,我们称之为A_ID和B_ID。现在,当前实现确保A_ID或B_ID为NULL,但不是两者。但是,我想知道是否有更好的方法来做到这一点。另外,存在多个V_i可以引用A或B中的相同条目的问题。

所以,我希望我的问题很清楚。有没有办法用关系数据库正确建模,而不依赖外部代码来强制执行约束?感谢您提前输入。

祝你好运 大卫

4 个答案:

答案 0 :(得分:2)

在关系理论中,一对一关系通常被转换为物理模型中的单个表。此单个表将包含来自两个表的行,您将使用检查约束来确定行的类型。这是迄今为止获得可靠的一对一关系的最简单方法。

答案 1 :(得分:1)

第一件事:在设计数​​据库时,表达记录与表之间的关系。 您从OO的角度来表达您的问题。这个范例不能用于设计表(SQL是一种声明性语言)。

否则,您可以在表格上添加约束来确保您的谓词。

也许Oracle提供了我不知道的其他可能性。

答案 2 :(得分:0)

模拟类的最常用方法 - rdbs中的实例关系是 Class = table 实例=行

考虑一下:为每个新实例插入一个新行;在不插入数据的地方,插入默认值,为您提供类数据;和触发器为您提供类级别的行为。

或者,给A和B指定相同的主键,并将B的PK设置为A的PK。当B中包含一行时,DBMS将检查是否存在“父”行A.可能需要绘图

+--------+   +--------+
|Table A |   |Table B |
+--------+   +--------+
|id (PK) |<--|id* (PK)|
|col1    |   |colB1   |
| ...    |   | ...    |
+--------+   +--------+

答案 3 :(得分:0)

前言:这是一个糟糕的设计,正如其他人所说的那样。

<强>假设:

create table a (a_id number primary key); 

create table b (b_id number primary key); 

create table v1 
(v1_id number primary key, a_id number references a, b_id number references b);

create table v2
(v2_id number primary key, a_id number references a, b_id number references b);

create table v3
(v3_id number primary key, a_id number references a, b_id number references b);

在任何V_i表中强制要求A或B中只有一个ID(但不是两者)非常容易。

alter table V1
  add constraint v1_check check
  (    (a_id is null and b_id is not null)
    or (a_id is not null and b_id is null)
  );

如果要扩展该约束,以便恰好存在A或B中的一个ID,并且该值仅存在于一行中:

create unique index v1_check_unique on v1  ( coalesce (a_id, b_id) );

困难的部分是确保A和B中的ID存在于V_i个表中的一个且仅存在一个create materialized view log on v1 with rowid; create materialized view log on v2 with rowid; create materialized view log on v3 with rowid; CREATE MATERIALIZED VIEW CROSS_TABLE REFRESH FAST ON COMMIT AS SELECT V1_ID AS V_ID, 'V1' AS TABLE_NAME, ROWID AS ROW_ID, COALESCE (A_ID, B_ID) AS OTHER_ID FROM V1 UNION ALL SELECT V2_ID AS V_ID, 'V2' AS TABLE_NAME, ROWID AS ROW_ID, COALESCE (A_ID, B_ID) AS OTHER_ID FROM V2 UNION ALL SELECT V3_ID AS V_ID, 'V3' AS TABLE_NAME, ROWID AS ROW_ID, COALESCE (A_ID, B_ID) AS OTHER_ID FROM V3 / ALTER TABLE CROSS_TABLE ADD CONSTRAINT CROSS_TABLE_UNIQUE UNIQUE (OTHER_ID); 表中。这不能在DML时完成,但可以在提交时强制执行。

INSERT INTO A VALUES (1);
INSERT INTO B VALUES (1);
INSERT INTO V1 (V1_ID, A_ID, B_ID) VALUES (1, 1, NULL);
INSERT INTO V2 (V2_ID, A_ID, B_ID) VALUES (1, 1, NULL);
COMMIT;

这似乎有效 - 但并不像你希望的那样令人敬畏。 Oracle无法在语句时间跨表强制执行该唯一性,因为会话A不允许考虑其他会话可能进行的任何其他更改。它只能在提交时强制执行该唯一性。

以下测试用例在针对空表运行时失败 - 并回滚整个事务,因为它无法推断导致失败的原因。注意事项。

{{1}}