如何关联同一表中的两行


11

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

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

这些方式的主要利弊是什么,当然,有什么我没想到的方式吗?

这是一个可使用的SQLFiddle:http ://sqlfiddle.com/#!12/7ee1a/1/0 。(由于这是我正在使用的,所以它是PostgreSQL,但我认为这个问题不是非常特定于PostgreSQL的。)作为示例,它当前存储了关系及其反向关系。


给定值可以彼此相关吗?给定值总是与另一个相关吗?它们是否共享相同的其他公共数据?
菲尔ᵀᴹ

是的,它们可以与不止1个其他行相关。不,它们不一定总是与另一行相关。他们不一定有任何公共数据。谢谢。
jpmc26 2013年

哎呀 忘了@Phil。还进行了编辑以添加刚出现在我身上的潜在结构。
jpmc26 2013年

Answers:


9

您设计的是好的。需要添加的是使关系无方向的约束。因此,如果没有添加(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
);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.