与多个表中的一个建立一对一关系

发布于 2024-11-01 00:50:39 字数 664 浏览 5 评论 0原文

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

好的,这就是实际问题:如何正确建模?我主要关心的是 V_i 中的每条记录不得同时链接到 A 和 B。它必须与 A 或 B 建立 1 对 1 的关系。此外,V_i 和 V_j 不得链接到 A 或 B 中的同一记录B. 我不知道如何正确地做到这一点。当前的结构如下所示:

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

所以,我希望我的问题是清楚的。有没有一种方法可以使用关系数据库对此进行正确建模,而不依赖外部代码来强制执行约束?感谢您提前提供意见。

此致 大卫

OK, I hope I'll be able to make clear what my problem is:
I have a database with 5 tables. Let's call them A and B, V_1, V_2, and V_3. A and B represent a list of things to be done. These actions are described in the V_i tables. Now, A represents sort of a template of stuff that has to be done with a certain type of item. B, on the other hand, describes what has to be done (or has been done) with a concrete instance if the abstract item described by A. So in OOP terminology one might say that A represents a class and B represents an instance of A. Whenever something is inserted into table B, the related data from table A is copied, so that it can be modified for that specific item without affecting A.

Okay, so here is the actual problem: How do I model this properly? My main concern is that each record in V_i must not be linked to both A and B. It has to be a 1 to 1 relationship with EITHER A OR B. Also, V_i and V_j must not be linked to the same record in A or B. I have no clue how to do this properly. The current structure looks like this:

A and B have a PK called ID. Each V_i also has a PK called ID and two FKs that referene A or B, let's call them A_ID and B_ID. Now, the current implementation ensures that either A_ID or B_ID is NULL, but not both. However, I was wondering if there is a better way to do this. Additionally, there is the problem that multiple V_i could reference the same entry in A or B.

So, I hope my problem is clear. Is there a way to properly model this with relational databases without relying on external code to enforce the constraints? Thanks for your input in advance.

Best regards
David

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

玩心态 2024-11-08 00:50:39

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

In relational theory, one-to-one relationships are generally translated to a single table in the physical model. This single table would contain rows from both tables and you would use check constraints to determine the type of the row. This is by far the simplest way to get reliable 1-to-1 relationships.

究竟谁懂我的在乎 2024-11-08 00:50:39

首先:设计数据库时,您表达的是记录之间的关系而不是表之间的关系。
您正在用面向对象的观点表达您的问题。这种范例不能用于设计表(SQL 是一种声明性语言)。

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

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

First thing: when designing a database, you express relations between records not tables.
You are expressing your problem with an OO point of view. This paradigm cannot be used to design tables (SQL being a declarative language).

Otherwise, you can add constraints on your table ensuring your predicate.

Maybe Oracle offers other possibilities I don't know.

梦在深巷 2024-11-08 00:50:39

在rdbs中建模类-实例关系的最常见方法是
类=表
Instance = row

考虑一下:为每个新实例插入一个新行;如果您不插入数据,则会插入默认值,这将为您提供类数据;和触发器给你班级级别的行为。

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

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

The most common way to model the class - instance relationship in rdbs is
Class = table
Instance = row

Think about it: you insert a new row for each new instance; where you do not insert data, defaults are inserted, which give you class data; and triggers give you class-level behaviours.

Alternatively, give A and B the same primary key, and set the PK of B to be an FK to the PK of A. When a row is included in B, the DBMS will check that a "parent" row exists in A. Probably needs drawing

+--------+   +--------+
|Table A |   |Table B |
+--------+   +--------+
|id (PK) |<--|id* (PK)|
|col1    |   |colB1   |
| ...    |   | ...    |
+--------+   +--------+
桃气十足 2024-11-08 00:50:39

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

假设

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 表的。这无法在 DML 时完成,但可以在提交时强制执行。

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);

这似乎有效——但没有你希望的那么好。 Oracle 无法在语句时强制执行表的唯一性,因为不允许会话 A 考虑其他会话可能进行的任何其他更改。它只能在提交时强制执行该唯一性。

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

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;

Preface: This is a bad design, as others have noted.

Assumptions:

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);

Mandating that in any of the V_i tables that exactly one of the ids from A or B is required (but not both) is pretty easy.

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)
  );

If you want to extend that constraint so that exactly one of the ids from A or B is present and that value exists in one and only one row:

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

The hard part is making sure that the ids from A and B exist in one and only one of the V_i tables. That can't be done at DML time, but it can be enforced at commit time.

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);

This appears to work - but not as awesomely as you'd hope. Oracle can't enforce that uniqueness across the tables at statement time because session A isn't allowed to take into account any other changes other sessions might be making. It can only enforce that uniqueness at commit time.

The following test case fails when run against empty tables - and rolls back the entire transaction, as it can't deduce which is causing the failure. Caveat emptor.

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文