如何在PostgreSQL中实现多对多关系?

157

我相信标题已经很清楚了。如何在PostgreSQL中创建表结构以建立多对多关系。

我的示例:

Product(name, price);
Bill(name, date, Products);

4
从账单表中移除产品,创建一个名为“bill_products”的新表,该表包含两个字段:一个指向产品,一个指向账单。将这两个字段设为新表的主键。 - Marc B
所以bill_products(bill, products);?他们两个都是PK吗? - Radu Gheorghiu
2
是的。它们将分别成为指向各自表的FK,并且一起成为新表的PK。 - Marc B
那么,bill_product(product引用product.name,bill引用bill.name,(product,bill)主键)是这样的吗? - Radu Gheorghiu
他们会指向产品和账单表的PK字段。 - Marc B
在表格之间创建数据透视表。 - mercury
1个回答

440
SQL DDL(数据定义语言)语句可能如下所示:

CREATE TABLE product (
  product_id serial PRIMARY KEY  -- implicit primary key constraint
, product    text NOT NULL
, price      numeric NOT NULL DEFAULT 0
);

CREATE TABLE bill (
  bill_id  serial PRIMARY KEY
, bill     text NOT NULL
, billdate date NOT NULL DEFAULT CURRENT_DATE
);

CREATE TABLE bill_product (
  bill_id    int REFERENCES bill (bill_id) ON UPDATE CASCADE ON DELETE CASCADE
, product_id int REFERENCES product (product_id) ON UPDATE CASCADE
, amount     numeric NOT NULL DEFAULT 1
, CONSTRAINT bill_product_pkey PRIMARY KEY (bill_id, product_id)  -- explicit pk
);

我做了一些调整:
  • n:m关系通常由一个单独的表实现,本例中为bill_product

  • 我添加了serial列作为替代主键。在Postgres 10或更高版本中,请考虑使用IDENTITY。请参阅:

    我强烈推荐这样做,因为产品名称很难唯一(不是一个好的“自然键”)。而且,使用4字节的integer(甚至是8字节的bigint)存储的字符串相比,强制唯一性并在外键中引用该列通常更便宜。

  • 不要使用基本数据类型的名称,例如date作为标识符。虽然这是可能的,但这是不好的风格,并会导致混淆的错误和错误消息。使用合法的小写未加引号的标识符。永远不要使用保留字,如果可以的话避免使用双引号混合大小写的标识符。

  • “name”不是一个好的名称。我将表product的列重命名为product(或product_name或类似)。这是一个更好的命名约定。否则,在查询中连接几个表时 - 在关系数据库中经常会这样做 - 你会得到多个名为“name”的列,并且必须使用列别名来解决混乱。那不是有帮助的。另一个广泛存在的反模式是只使用“id”作为列名。
    我不确定“bill”的名称是什么。在这种情况下,bill_id可能足够了。

  • pricenumeric数据类型,以精确存储小数(任意精度类型而不是浮点类型)。如果你只处理整数,那就是integer。例如,你可以将价格保存为分

  • amount(在你的问题中称为“Products”)进入链接表bill_product,并且也是numeric类型。同样,如果你只处理整数,那就是integer

  • 你看到了bill_product中的外键吗?我都创建了级联更改:ON UPDATE CASCADE。如果product_idbill_id发生变化,则将变化级联到bill_product中的所有相关条目,并且不会出现任何问题。这些只是没有自身重要性的引用。
    我还为bill_id使用了ON DELETE CASCADE:如果删除账单,则其详细信息也会被删除。
    但对于产品来说不是这样的:你不想删除在账单中使用的产品。如果你尝试这样做,Postgres会抛出一个错误。你应该向product


我该如何为映射表bill_product创建索引?通常应该是这样的:CREATE INDEX idx_bill_product_id ON booked_rates(bill_id, product_id)。这样做正确吗? - codyLine
2
@codyLine:此索引是由主键自动生成的。 - Erwin Brandstetter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接