有 3 张桌子:
A
、B
、C
。
A
中的行可以与 B
中的行关联,也可以与 C
中的行关联,但不能同时与 B
和 C
关联。
换句话说,关联是异或、一对一的。
以下是一些建议的替代方案:
B
持有A
(B.A_id
) 的外键,C
持有A
(C.A_id
) 的外键。我想不出有什么办法可以限制协会的排他性。A
拥有一个指向 B
(A.B_id
) 的外键,以及另一个指向 C
(A.C_id
) 的外键。我们可以通过CHECK (A_id IS NULL AND B_id IS NOT NULL) OR (A_id IS NOT NULL AND B_id IS NULL)
来限制排他性。但我们按宽度(内存和磁盘)付费,因为我们不使用一半的外键列。A
保存一个不受约束的列 (A.foreign_id
),它可以是 B
或 C
的键,并且 A
保存表示类型的另一列(A.foreign_type
= B
或C
)。这满足了排他性约束,并最小化了宽度,但失去了外键约束验证,并使查询变得复杂(我加入的人是基于 A.foreign_type
列)哪个规模最好?是否有未列出的优点/缺点可供讨论?还有其他替代方案可以考虑吗?
替代方案 1. 这种方法的问题在于
A
可能包含与 B
或 C
中不同的内容。如果您可以接受这种情况,那么以下设计(概念上)可能是您的排他性问题的解决方案:
CREATE TABLE A
(
type SMALLINT,
id INTEGER,
PRIMARY KEY (type, id)
);
CREATE TABLE B
(
type SMALLINT,
id INTEGER,
PRIMARY KEY (type, id),
CHECK ( type = 1 ),
FOREIGN KEY (type, id) REFERENCES A (type, id)
);
CREATE TABLE C
(
type SMALLINT,
id INTEGER,
PRIMARY KEY (type, id),
CHECK ( type = 2 ),
FOREIGN KEY (type, id) REFERENCES A (type, id)
);
替代方案 2. 这种方法的问题在于
B
和 C
可能包含 A
不会包含的内容。如果这对您来说不是问题,那么我根本不用担心磁盘空间(检查这一点的最佳方法是进行实验)。
替代方案 3. 如果您想要有限制,请考虑制作一张表
BC
而不是两张表 B
和 C
。
CREATE TABLE BC
(
type SMALLINT,
id INTEGER,
b_field INT,
c_field INT,
PRIMARY KEY (type, id)
);
CREATE TABLE A
(
type SMALLINT,
id INTEGER,
PRIMARY KEY (type, id),
FOREIGN KEY (type, id) REFERENCES BC (type, id)
);