Postgres复合主键依赖问题

时间:2014-10-28 20:25:40

标签: sql postgresql foreign-keys primary-key composite-primary-key

我正在马里兰州的约翰霍普金斯大学学习数据库,并有一个问题。我已经通过电子邮件发送了我的教授,他知道我在这里问这个问题而且他很酷。所以我正在Postgres开发一个COOKBOOK数据库,我在Postgres中遇到了一个有趣的问题,我似乎无法构建PRICE表。我有一个很好的食谱ERD,但显然不能发布,直到我的声誉至少10.我会尽我所能描述ERD。有三个与PRICE有关的保理表。这些是成分,替代品和价格。

这是ERD的链接(请注意1:M代表成分替换): [ERD]

我可以拥有一个可能有多个替代品的成分,并且可能在SUBSTITUTION和PRICE之间一对一(如果已知PRICE,则每个替换一个PRICE)。如果PRICE已知,那么PRICE能够定义具有复合主键的元组:(price_id,ingredient_id(fk),substitution_id(fk))

我面临的挑战是Postgres SQL不允许我建立这种关系,我不确定为什么。我已经将SUBSTITUTION中的键设置为具有UNIQUE约束,因此不应该是问题。我唯一能想到的是,SUBSTITUTION中的ingredient_id是INGREDIENT的外键,因此可能无法在SUBSTITUTION中实际建立,但我得到的错误并不表明这一点。这就是我在终端中获得的内容(首先描述了SUBSTITUTION ):

   cookbook=# \d+ SUBSTITUTION
                                                             Table "public.substitution"
   Column       |         Type          |                                Modifiers                                 | Storage  | Description 
   --------------------+-----------------------+--------------------------------------------------------------------------+----------+-------------
   substitution_id    | integer               | not null default nextval('subsitution_substitution_id_seq'::regclass)    | plain    | 
   ingredient_id      | integer               | not null default nextval('subsitution_ingredient_id_seq'::regclass)      | plain    | 
   name               | character varying(50) | not null                                                                 | extended | 
   measurement_ref_id | integer               | not null default nextval('subsitution_measurement_ref_id_seq'::regclass) | plain    | 
   metric_unit        | character varying(25) | not null                                                                 | extended | 
   Indexes:
   "subsitution_pkey" PRIMARY KEY, btree (substitution_id, ingredient_id)
   "uniqueattributes" UNIQUE, btree (substitution_id, ingredient_id)
   Foreign-key constraints:
   "subsitution_ingredient_id_fkey" FOREIGN KEY (ingredient_id) REFERENCES ingredient(ingredient_id)
   "subsitution_measurement_ref_id_fkey" FOREIGN KEY (measurement_ref_id) REFERENCES measurement_ref(measurement_ref_id)
   Has OIDs: no

   cookbook=# create table price(
   price_id serial not null,
   ingredient_id serial references substitution(ingredient_id),
   cookbook(# substitution_id serial references substitution(substitution_id),
   cookbook(# usdollars smallint not null,
   cookbook(# availability season,
   cookbook(# seasonal boolean,
   cookbook(# primary key (price_id, ingredient_id, substitution_id)
   cookbook(# );
   NOTICE:  CREATE TABLE will create implicit sequence "price_price_id_seq" for serial column "price.price_id"
   NOTICE:  CREATE TABLE will create implicit sequence "price_ingredient_id_seq" for serial column "price.ingredient_id"
   NOTICE:  CREATE TABLE will create implicit sequence "price_substitution_id_seq" for serial column "price.substitution_id"
   NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "price_pkey" for table "price"
   ERROR:  there is no unique constraint matching given keys for referenced table "substitution"

3 个答案:

答案 0 :(得分:3)

' ave省略了一些列,因此您可以专注于键。我几乎没看一眼你的ERD。 (我讨厌一千个太阳的燃烧激情的NERD。)

create table ingredients (
  ingredient_id serial primary key,
  -- Don't allow duplicate names. 
  ingredient_name varchar(35) not null unique
);

create table substitutions (
  -- These are properly declared integer, not serial. 
  -- Also note two separate foreign key references.
  ingredient_id integer not null references ingredients (ingredient_id),
  substitute_id integer not null references ingredients (ingredient_id),
  primary key (ingredient_id, substitute_id)
);

create table prices (
  -- Price id number is unnecessary.
  ingredient_id integer not null,
  substitute_id integer not null,
  -- Money is usually declared numeric(n, m) or decimal(n, m).
  us_dollars numeric(10, 2) not null 
    -- Negative amounts don't make sense.
    check (us_dollars >= 0),
  -- Only one row per distinct substitution.
  primary key (ingredient_id, substitute_id),
  -- One single foreign key reference, but it references *two* columns.
  foreign key (ingredient_id, substitute_id) references substitutions (ingredient_id, substitute_id)
);

答案 1 :(得分:0)

问题是因为FOREIGN KEYprice中定义的substitution(ingredient_id)不是唯一的。

substitution表中,您定义了以下索引:

  "subsitution_pkey" PRIMARY KEY, btree (substitution_id, ingredient_id)
   "uniqueattributes" UNIQUE, btree (substitution_id, ingredient_id)

这意味着对于该表,唯一性目前需要(substitution_id, ingredient_id)元组。另外,这两个索引实际上是彼此重复的,因为PRIMARY KEY约束通过定义来保证唯一性。

所以,你有各种各样的选择,但我发现最简单的事情就是经常使用一个为每个表定义的唯一ID - 我喜欢使用id serial,这将创建一个隐含的序列,然后将其定义为PRIMARY KEY

然后,您只需使用该键定义FOREIGN KEY关系。在使用多键元组执行此操作时,它确实使事情变得复杂,并且我发现使用单个ID更容易。如果需要SELECT性能等,您可以随时在多键元组上创建其他唯一索引。

同样的事情适用于ingredient_id的FK约束 - 它不是唯一的。我上面提到的同样类型的补救措施也适用于该栏目。

答案 2 :(得分:-2)

**更新:2014-11-02决定放弃PRICE表。我的教授说,将两个独立的价格属性吸收到INGREDIENT和SUBSTITUTION实体类型中会很好。他说我像往常一样努力思考。 **

解决了问题!感谢所有人的反馈。这个网站非常好。这是最终的解决方案布局:

按此顺序构建INGREDIENT,SUBSTITUTION和PRICE ......

   create table INGREDIENT(
      ingredient_id serial not null,
      description text,
      amount smallint,
      measurement_ref_id serial references measurement_ref(measurement_ref_id),
      nutritional_info_id serial references nutritional_info(nutritional_info_id),
      primary key (ingredient_id)
   );

   create table SUBSTITUTION(
      substitution_id serial not null unique,
      ingredient_id serial not null references ingredient(ingredient_id),
      name varchar(50) not null,
      measurement_ref_id serial references measurement_ref(measurement_ref_id),
      metric_unit varchar(25) not null,
      primary key (substitution_id, ingredient_id)
   );

   -- NOTE: I'll add the other variables to this later.
   CREATE TABLE PRICE(
      ingredient_id serial not null,
      substitution_id serial not null,
      -- link the two keys from substitution in as two unique foreign keys
      foreign key (ingredient_id, substitution_id) references substitution (ingredient_id, substitution_id),   
      -- combine foreign keys as one single composite primary key
      primary key (ingredient_id, substitution_id)
   );

如果有人有兴趣,我会在我的所有SQL脚本中添加一个github并链接。我正在制作一个带有ERD和12个表的COOKBOOK数据库。应该是一个有趣的项目。再次感谢您的帮助。