是否有针对不同项目的联合表的模式?

时间:2018-05-16 19:34:19

标签: postgresql

我希望基于列约束的2列组合。我在这里找不到使用外键的方法,因为它应该是有条件的FK。希望这个基本的SQL显示问题:

CREATE TABLE performer_type (
  id serial primary key,
  type varchar
);

INSERT INTO performer_type ( id, type ) VALUES (1, 'singer'), ( 2, 'band');

CREATE TABLE singer (
  id serial primary key,
  name varchar
);

INSERT INTO singer ( id, name ) VALUES (1, 'Robert');

CREATE TABLE band (
  id serial primary key,
  name varchar
);

INSERT INTO band ( id, name ) VALUES (1, 'Animates'), ( 2, 'Zed Leppelin');

CREATE TABLE gig (
  id serial primary key,
  performer_type_id int default null, /* FK, no problem */
  performer_id int default null       /* want FK based on previous FK, no good solution so far */
);

INSERT INTO gig ( performer_type_id, performer_id ) VALUES ( 1,1 ), (2,1), (2,2), (1,2), (2,3);

现在,最后一次INSERT工作,但是对于最后2个值对,我希望它失败,因为没有歌手ID 2和带ID 3.如何设置这样的约束?

我已经问过类似的question in Mysql context,唯一的解决办法是使用触发器。触发器的问题是:您不能拥有类型和表集的动态列表。我想动态添加类型(和相关表格)。

我也找到very promising pattern,但这对我来说是颠倒的,我没想通,如何让它在我的情况下发挥作用。

我在这看起来似乎对我这么有用的模式,我认为必须有一些共同的方式。是吗?

修改

似乎,我在我的示例中选择了不好的项目,所以我尝试说清楚:不同的表演者表(singerband)之间有 NO 关系。 gig - 表只需列出不同表演者的任务,而不必在他们之间设置任何关系。

另一个例子是库存中的物品:我可能有item_type - 表,它定义了数百种具有相关表格的项目类型(例如,orangehouse),并且应该是表格stock,其中包含所有项目的外观。

我使用的PostgreSQL是9.6

1 个答案:

答案 0 :(得分:0)

基于@Laurenz Albe answer我在上面形成了一个解决方案。主要区别:有父表performer,PK是特定执行者表的FK / PK,也可以从gig表中引用。

CREATE TABLE performer_type (
  id serial primary key,
  type varchar
);
INSERT INTO performer_type ( id, type ) VALUES (1, 'singer' ), ( 2, 'band' );

CREATE TABLE performer (
  id serial primary key,
  performer_type_id int REFERENCES performer_type(id)
);

CREATE TABLE singer (
  id int primary key REFERENCES performer(id),
  name varchar
);

INSERT INTO performer ( performer_type_id ) VALUES (1); -- get PK 1 for next statement
INSERT INTO singer ( id, name ) VALUES (1, 'Robert');

CREATE TABLE band (
  id int primary key REFERENCES performer(id),
  name varchar
);

INSERT INTO performer ( performer_type_id ) VALUES (2); -- get PK 2 for next statement
INSERT INTO singer ( id, name ) VALUES (2, 'Animates');
INSERT INTO performer ( performer_type_id ) VALUES (2); -- get PK 3 for next statement
INSERT INTO singer ( id, name ) VALUES (3, 'Zed Leppelin');

CREATE TABLE gig (
  id serial primary key,
  performer_id int REFERENCES performer(id)
);

INSERT INTO gig ( performer_id ) VALUES (1), (2), (3), (4);

最后一次INSERT失败,正如所料:

ERROR:  insert or update on table "gig" violates foreign key constraint "gig_performer_id_fkey"
DETAIL:  Key (performer_id)=(4) is not present in table "performer".

<强>但是

对我来说有一个恼人的问题:我没有办法区分哪个ID用于歌手,哪个用于乐队等(原始示例中我在performer_type_id中有gig - 表格为),因为任何performer_id都可能属于任何表演者。所以我想任何表演者类型都有它自己的ID范围,所以我为每个序列创建虚拟表

CREATE TABLE band_id (
  id int primary key,
  dummy boolean default null
);
CREATE SEQUENCE band_id_seq START 1;
ALTER TABLE band_id ALTER COLUMN id SET DEFAULT nextval('band_id_seq');

CREATE TABLE singer_id (
  id int primary key,
  dummy boolean default null
);
CREATE SEQUENCE singer_id_seq START 2000000;
ALTER TABLE singer_id ALTER COLUMN id SET DEFAULT nextval('singer_id_seq');

现在,要在特定的perfomer表中插入新行,我必须为它获取下一个ID:

INSERT INTO band_id (dummy) VALUES (NULL);

试图弄清楚,是否可以在数据库级别上解决此过程,或者在应用程序级别中完成某些任务。如果插入band表可以:

,那就太好了
  • 在触发插入band_id以生成特定ID
  • 之前
  • 在触发将此新ID插入performer - 表
  • 之前
  • 将此新ID包含在band
  • 的INSERT中

第2点很容易,但最后一点目前尚不清楚。