您设计的是好的。需要添加的是使关系无方向的约束。因此,如果没有添加(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)
);
测试于:SQL-Fiddle
如果您尝试添加一行(1,5)
:
INSERT INTO bridge_x VALUES
(1,5) ;
它失败并显示:
错误:对表“ bridge_x”的插入或更新违反了外键约束“ x_x_directionless”
详细信息:表(bridge_x)中不存在键(x_id2,x_id1)=(5,1)。:
插入到bridge_x值(1,5)
此外,CHECK
如果要禁止(y,y)
行,则可以添加约束:
ALTER TABLE bridge_x
ADD CONSTRAINT x_x_self_referencing_items_not_allowed
CHECK (x_id1 <> x_id2) ;
正如您提到的,还有其他方法可以实现此目的,例如通过强制输入中的较低ID x_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
);