我如何强制两个子 table 之间的耦合 table 引用同一个主 table?
How do I enforce that a coupling table between two subtables reference the same master table?
我有一个 table person
和两个 table foo
和 bar
都引用 person
外键名为 person_id
。我需要创建一个耦合 table,将一个 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 Fiddle: http://sqlfiddle.com/#!17/40c78/3
您可以在包含 id
和 person_id
的 foo
和 bar
上创建唯一约束。如果 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)
是自然主键,可以保证不会输入任何关系 more不止一次。
您偶然发现了单个代理键的主要问题:当涉及层次结构时(例如 foo_bar 是 foo 和 bar 的子级,它们都是 person 的子级),数据库系统无法强制一致性.
所以改用复合键。类似于(伪代码):
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));
复合键的缺点是使连接更容易出错(即您可能会混淆关键部分或遗漏键的一部分),但它们也通过强制一致性使数据库变得更好。
我有一个 table person
和两个 table foo
和 bar
都引用 person
外键名为 person_id
。我需要创建一个耦合 table,将一个 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 Fiddle: http://sqlfiddle.com/#!17/40c78/3
您可以在包含 id
和 person_id
的 foo
和 bar
上创建唯一约束。如果 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)
是自然主键,可以保证不会输入任何关系 more不止一次。
您偶然发现了单个代理键的主要问题:当涉及层次结构时(例如 foo_bar 是 foo 和 bar 的子级,它们都是 person 的子级),数据库系统无法强制一致性.
所以改用复合键。类似于(伪代码):
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));
复合键的缺点是使连接更容易出错(即您可能会混淆关键部分或遗漏键的一部分),但它们也通过强制一致性使数据库变得更好。