jpm*_*c26 12 foreign-key database-design
我有一个表格,其中的行可以相互关联,从逻辑上讲,两行之间的关系是双向的(基本上是无方向的)。(如果您想知道,是的,这确实应该是一张表。它是完全相同的逻辑实体/类型的两个事物。)我可以想到几种方法来表示这一点:
这些方式有哪些主要的优缺点,当然,还有什么我没有想到的方式?
这是一个可以玩的 SQLFiddle:http ://sqlfiddle.com/#!12/7ee1a/1/0 。(碰巧是 PostgreSQL,因为那是我正在使用的,但我认为这个问题不是 PostgreSQL 特有的。)它目前存储关系及其反向仅作为示例。
ype*_*eᵀᴹ 10
你设计的很好。需要添加的是使关系无方向的约束。因此,如果不添加(1,5)
一行,您就不能拥有一行(5,1)
。
这可以通过桥表上的自引用约束来实现*。
*:它可以在 Postgres、Oracle、DB2 和所有实现了 SQL 标准描述的外键约束的 DBMS 中完成(延迟,例如在事务结束时检查。)无论如何,延迟检查并不是真正需要的,就像在 SQL-在语句结束时检查它们的服务器,这种结构仍然有效。您不能在 MySQL 中执行此操作,因为“InnoDB 逐行检查 UNIQUE 和 FOREIGN KEY 约束”。
因此,在 Postgres 中,以下内容将符合您的要求:
CREATE TABLE x
(
x_id SERIAL NOT NULL PRIMARY KEY,
data VARCHAR(10) NOT NULL
);
CREATE TABLE bridge_x
(
x_id1 INTEGER NOT NULL REFERENCES x (x_id),
x_id2 INTEGER NOT NULL REFERENCES x (x_id),
PRIMARY KEY(x_id1, x_id2),
CONSTRAINT x_x_directionless
FOREIGN KEY (x_id2, x_id1)
REFERENCES bridge_x (x_id1, x_id2)
);
Run Code Online (Sandbox Code Playgroud)
测试地点:SQL-Fiddle
如果您尝试添加一行(1,5)
:
INSERT INTO bridge_x VALUES
(1,5) ;
Run Code Online (Sandbox Code Playgroud)
它失败了:
错误:在表“bridge_x”上插入或更新违反外键约束“x_x_directionless”
详细信息:表“bridge_x”中不存在键(x_id2,x_id1)=(5, 1)。:
INSERT INTO bridge_x VALUES(1,5)
此外,CHECK
如果要禁止(y,y)
行,可以添加约束:
ALTER TABLE bridge_x
ADD CONSTRAINT x_x_self_referencing_items_not_allowed
CHECK (x_id1 <> x_id2) ;
Run Code Online (Sandbox Code Playgroud)
正如您提到的,还有其他方法可以实现这一点,例如通过强制较低的 idx_id1
和较高的 id 在x_id2
列中仅存储关系的一个方向(在一行中,而不是两行)。它看起来更容易实现,但通常会导致更复杂的查询:
CREATE TABLE bridge_x
(
x_id1 INTEGER NOT NULL REFERENCES x (x_id),
x_id2 INTEGER NOT NULL REFERENCES x (x_id),
PRIMARY KEY(x_id1, x_id2),
CONSTRAINT x_x_directionless
CHECK (x_id1 <= x_id2) -- or "<" to forbid `(y,y)` rows
);
Run Code Online (Sandbox Code Playgroud)