如何强制两个子表之间的耦合表引用同一个主表?

问题描述 投票:1回答:2

我有一张桌子person和两张桌子foobar两个都用person和外键来引用person_id。我需要创建一个耦合表,将一个foo链接到一个bar,但两者都需要引用相同的person

如何使用声明性构造在关系结构中表达这一点?或者我是否需要创建一个触发器来强制执行此操作?

CREATE TABLE person
(id int primary key, name text);

INSERT INTO person
(id, name)
VALUES
(1, 'John'),
(2, 'Jane');

CREATE TABLE foo
(id int primary key, person_id int references person(id) not null, comment text);

INSERT INTO foo
(id, person_id, comment)
VALUES
(1, 1, 'John is great'),
(2, 2, 'Jane is great');

CREATE TABLE bar
(id int primary key, person_id int references person(id) not null, comment text);

INSERT INTO bar
(id, person_id, comment)
VALUES
(1, 1, 'John is super great'),
(2, 2, 'Jane is super great');

CREATE TABLE foo_bar
(id int primary key, foo_id int references foo(id), bar_id int references bar(id));

INSERT INTO foo_bar
(id, foo_id, bar_id)
VALUES
(1, 1, 1),
(2, 1, 2), -- Shouldn't be possible!
(3, 2, 1), -- Shouldn't be possible!
(4, 2, 2);

正如此查询所示,在foo_bar中的行引用John和Jane的数据时,完全可以获得结果:

select foo.comment, bar.comment from foo_bar
inner join foo ON foo.id = foo_bar.foo_id
inner join bar ON bar.id = foo_bar.bar_id;

结果:

John is great, John is super great
John is great, Jane is super great
Jane is great, John is super great
Jane is great, Jane is super great

SQL小提琴:http://sqlfiddle.com/#!17/40c78/3

sql database postgresql data-modeling
2个回答
1
投票

您可以在包含foobaridperson_id上创建一个唯一约束。如果foo_bar上的外键约束引用这些唯一约束,则自动满足条件。

ALTER TABLE foo ADD CONSTRAINT foo_id_person_unique
   UNIQUE (person_id, id);
ALTER TABLE bar ADD CONSTRAINT bar_id_person_unique
   UNIQUE (person_id, id);

ALTER TABLE foo_bar ADD person_id integer;

UPDATE foo_bar
SET person_id = foo.person_id
FROM foo
WHERE foo_bar.foo_id = foo_id;

ALTER TABLE foo_bar ALTER person_id SET NOT NULL;

ALTER TABLE foo_bar ADD CONSTRAINT foo_bar_foo_fkey
   FOREIGN KEY (person_id, foo_id) REFERENCES foo (person_id, id);
ALTER TABLE foo_bar ADD CONSTRAINT foo_bar_bar_fkey
   FOREIGN KEY (person_id, bar_id) REFERENCES bar (person_id, id);

然后从foo_bar中删除原始外键约束。

我不会为foo_bar使用人工主键,因为(foo_id, bar_id)是一个自然的主键,可以保证不会多次输入任何关系。


1
投票

您已经偶然发现了单个代理键的主要问题:当涉及到层次结构时(例如foo_bar对于foo和bar都是子对象,它们都是子对象),数据库系统无法强制执行一致性。

因此,使用复合键。某些东西(伪代码):

CREATE TABLE person (person_nr, name text,
  PRIMARY KEY (person_nr));

CREATE TABLE foo (person_nr, foo_nr, comment text,
  PRIMARY KEY (person_nr, foo_nr),
  FOREIGN KEY person_nr REFERENCES person(person_nr));

CREATE TABLE bar (person_nr, bar_nr, comment text,
  PRIMARY KEY (person_nr, bar_nr),
  FOREIGN KEY person_nr REFERENCES person(person_nr));

CREATE TABLE foo_bar (person_id, foo_nr, bar_nr,
  PRIMARY KEY (person_nr, foo_nr, bar_nr),
  FOREIGN KEY (person_nr, foo_nr) REFERENCES foo(person_nr, foo_nr),
  FOREIGN KEY (person_nr, bar_nr) REFERENCES bar(person_nr, bar_nr));

复合键的缺点是使连接更容易出错(即,您可能会混淆关键部分或错过密钥的一部分),但它们也通过强制执行一致性来使数据库更好。

© www.soinside.com 2019 - 2024. All rights reserved.