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

时间:2012-03-20 15:28:07

标签: sql database postgresql database-design many-to-many

我相信标题是不言自明的。如何在PostgreSQL中创建表结构以建立多对多关系。

我的例子:

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

1 个答案:

答案 0 :(得分:220)

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列添加为代理主键。我强烈建议,因为产品的名称并不是唯一的。此外,使用4字节integer而不是将字符串存储为textvarchar,强制执行唯一性并引用外键中的列要便宜得多。
    在Postgres 10或更高版本中,请考虑使用IDENTITY column。详细说明:

  • 请勿将date等基本数据类型的名称用作标识符。虽然这是可能的,但它是糟糕的风格,并导致混淆错误和错误消息。使用legal, lower case, unquoted identifiers。如果可以,请不要使用reserved words并避免使用双引号混合大小写标识符。

  • name不是一个好名字。我将表格name的{​​{1}}列重命名为product。这是一个更好的命名约定。否则,当你在一个查询中加入几个表时 - 你在关系数据库中很多 - 你最终得到了多个名为product的列,并且必须使用列别名来排序一团糟。那没用。另一种广泛的反模式只是name作为列名 我不确定id的名称是什么。在这种情况下,bill可以是名称

  • bill_id属于数据类型 price,可以按照输入(任意精度类型而不是浮点类型)存储小数 )。如果您专门处理整数,请设为numeric。例如,您可以将价格保存为Cents

  • integer(您问题中的amount)会进入链接表"Products",同时也是bill_product类型。如果您专门处理整数,请再次numeric

  • 您在integer中看到外键了吗?我创建了两个级联更改(bill_product):如果ON UPDATE CASCADEproduct_id应该更改,则更改会级联到bill_id中的所有相关条目,并且不会中断。\ br /> 我还使用bill_product ON DELETE CASCADE:如果您删除了帐单,则会删除详细信息 产品不是这样:您不想删除帐单中使用的产品。如果你尝试这个,Postgres会抛出一个错误。您可以向bill_id添加另一列,以标记过时的行。

  • 此基本示例中的所有列最终都是 product ,因此不允许NOT NULL值。 (是的,所有列 - 主键中使用的列自动定义为NULL。)这是因为UNIQUE NOT NULL值在任何列中都没有意义。它让初学者的生活更轻松。但是你不会轻易逃脱,无论如何你需要理解NULL handling。其他列可能允许NULL值,函数和联接可以在查询等中引入NULL值。

  • 阅读CREATE TABLE in the manual

  • 一章
  • 主键在键列上使用唯一的索引实现,这使得PK列上的条件快速查询。但是,键列的顺序与多列键相关。由于我的示例中的NULL上的PK位于bill_product,因此如果您有查询给定(bill_id, product_id),则可能只想在product_id(product_id, bill_id)添加另一个索引1}}而不是product_id。详细说明:

  • 阅读chapter on indexes in the manual