如何关联同一个表中的两行

jpm*_*c26 12 foreign-key database-design

我有一个表格,其中的行可以相互关联,从逻辑上讲,两行之间的关系是双向的(基本上是无方向的)。(如果您想知道,是的,这确实应该是一张表。它是完全相同的逻辑实体/类型的两个事物。)我可以想到几种方法来表示这一点:

  1. 存储关系及其反向
  2. 以一种方式存储关系,限制数据库以另一种方式存储它,并有两个 FK 顺序相反的索引(一个索引是 PK 索引)
  3. 用两个索引以一种方式存储关系,并允许插入第二个索引(听起来有点恶心,但嘿,完整性)
  4. 创建某种分组表,并在原始表上对其进行 FK。(提出了很多问题。分组表只会有一个数字;为什么还要有表?使 FK NULLable 或具有关联单行的组?)

这些方式有哪些主要的优缺点,当然,还有什么我没有想到的方式?

这是一个可以玩的 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)