好的,我希望我能说清楚我的问题是什么: 我有一个包含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中的相同条目的问题。
所以,我希望我的问题很清楚。有没有办法用关系数据库正确建模,而不依赖外部代码来强制执行约束?感谢您提前输入。
祝你好运 大卫
答案 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}}