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

我相信标题是不言自明的。 你如何在PostgreSQL中创build表结构来build立一个多对多的关系。

我的例子:

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

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列作为代理主键 。 我强烈build议,因为产品的名称不是唯一的。 此外,在外键中强制唯一性和引用列对于使用4字节的integer比使用textvarchar存储的string要便宜得多。
    在Postgres 10或更高版本中,请考虑使用IDENTITY列 。 细节:

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

  • name不是一个好名字。 我将表格productname栏更名为product 。 这是一个更好的命名约定 。 否则,当你在一个查询中join了几个表格时(你在关系数据库中做了很多工作) ,最终会得到多个名为name列,并且必须使用列别名来处理这个混乱。 这没有帮助。 另一个广泛的反模式将只是id作为列名。
    我不确定bill的名字是什么。 也许bill_id可以是这种情况下的名字

  • price数据types的 numeric精确地存储input的分数(任意精度types,而不是浮点types)。 如果你完全处理整个数字,那么使用这个integer 。 例如,您可以将价格节省为美分

  • amount (您的问题中的"Products" )进入链接表bill_product ,也是numerictypes。 再次, integer如果你处理完整的数字。

  • 你看到bill_product外键 ? 我创build了两个级联更改( ON UPDATE CASCADE ):如果product_idbill_id应该更改,则更改会级联到bill_product所有相关的条目,并且不会有任何中断。
    我还使用了ON DELETE CASCADE for bill_id :如果您删除一个帐单,详细信息将被删除。
    对于产品不是这样的:您不想删除账单中使用的产品。 如果你尝试这个,Postgres会抛出一个错误。 您可以将另一列添加到product来标记废弃的行。

  • 这个基本示例中的所有列最终都是NOT NULL ,因此不允许使用NULL值。 (是的, 所有列 – 主键中使用的列自动定义为UNIQUE NOT NULL 。)这是因为NULL值在任何列中都没有意义。 这使初学者的生活更轻松。 但是你不会轻易离开,无论如何你需要理解NULL处理 。 额外的列可能允许NULL值,函数和连接可以在查询中引入NULL

  • 阅读手册中关于CREATE TABLE的章节。

  • 主键在键列上使用唯一索引实现,这使得快速查询PK列中的条件。 但是,键列的顺序与多列键相关。 由于在我的示例中, bill_product上的PK开(bill_id, product_id) ,如果您有查询给定product_id且没有bill_id查询,则可能需要在product_id(product_id, bill_id)上添加另一个索引。 细节:

    • PostgreSQL组合主键
    • 一个复合索引是否也适用于第一个字段的查询?
    • 在PostgreSQL中使用索引
  • 阅读手册中关于索引的章节 。