如何强制由两个外键组成的复合主键的唯一性到同一个表?

man*_*iam 8 postgresql database-design primary-key unique-constraint

我有两张桌子:一张user桌子和一张friendship桌子。假设friendship表格如下所示:

friendship
------------
user_one_id
user_two_id
other_fields
Run Code Online (Sandbox Code Playgroud)

我需要强制执行和忽略ordering值组合的唯一性,因此:(user_one_id, user_two_id)

user_one_id | user_two_id
------------+------------
          1 |          2
          2 |          1  -- the DBMS should throw a unique constraint error when trying to insert a row like this one.
Run Code Online (Sandbox Code Playgroud)

另外重要的一点是,user_one_id代表发起friendship,并user_two_id代表收件人,所以我不能只是做了两个“小”的ID user_one_id

问题

有没有办法使用约束来做到这一点,还是应该以其他方式实现?

Eva*_*oll 8

根据你的评论,

对于我的用例,user_one_id 代表友谊的发起者,而 user_two_id 代表友谊的接收者。所以我不能只使用最低值作为 user_one_id。

嗯,你仍然可以做到。您的用例只是排除了行约束以确保这一点。你想要的是使用表格约束,就像这样。

CREATE TABLE friendship (
  user_one_id int NOT NULL,
  user_two_id int NOT NULL,
  CHECK (user_one_id != user_two_id ),
  PRIMARY KEY (user_one_id, user_two_id)
);
-- you can do least first if you want. doesn't matter.
CREATE UNIQUE INDEX ON friendship (
  greatest(user_one_id, user_two_id),
  least(user_one_id, user_two_id)
);
Run Code Online (Sandbox Code Playgroud)

我们有很多事情要做……我们确保。

  1. 两者都是 NOT NULL
  2. 两者不相等
  3. 两者都是 UNIQUE (user_one_id, user_two_id)

这留下了一个剩余的交换唯一性问题,我们使用索引实现的自定义唯一表约束来解决该问题。

布丁中的证明

INSERT INTO friendship VALUES ( 1,2 );
INSERT 0 1
test=# INSERT INTO friendship VALUES ( 2,1 );
ERROR:  duplicate key value violates unique constraint friendship_greatest_least_idx"
DETAIL:  Key ((GREATEST(user_one_id, user_two_id)), (LEAST(user_one_id, user_two_id)))=(2, 1) already exists.
Run Code Online (Sandbox Code Playgroud)

作为一个重要的友好说明,你的名字很愚蠢。关系很好。在生产中,给它们起更好的名字..

friendship
----------
request_initiator
request_target
other_fields
Run Code Online (Sandbox Code Playgroud)

  • 虽然,现在我想起来了,大多数现实世界的数据库无论如何都可能会从第二个索引中受益。 (2认同)