如何在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
比使用text
或varchar
存储的string要便宜得多。
在Postgres 10或更高版本中,请考虑使用IDENTITY
列 。 细节: -
不要使用
date
等基本数据types的名称作为标识符 。 虽然这是可能的,但它是不好的风格,并导致混淆错误和错误信息。 使用合法的,小写的,不加引号的标识符 。 如果可以的话 ,切勿使用保留字并避免使用双引号混合大小写标识符。 -
name
不是一个好名字。 我将表格product
的name
栏更名为product
。 这是一个更好的命名约定 。 否则,当你在一个查询中join了几个表格时(你在关系数据库中做了很多工作) ,最终会得到多个名为name
列,并且必须使用列别名来处理这个混乱。 这没有帮助。 另一个广泛的反模式将只是id
作为列名。
我不确定bill
的名字是什么。 也许bill_id
可以是这种情况下的名字 。 -
price
是数据types的numeric
以精确地存储input的分数(任意精度types,而不是浮点types)。 如果你完全处理整个数字,那么使用这个integer
。 例如,您可以将价格节省为美分 。 -
amount
(您的问题中的"Products"
)进入链接表bill_product
,也是numeric
types。 再次,integer
如果你处理完整的数字。 -
你看到
bill_product
的外键 ? 我创build了两个级联更改(ON UPDATE CASCADE
):如果product_id
或bill_id
应该更改,则更改会级联到bill_product
所有相关的条目,并且不会有任何中断。
我还使用了ON DELETE CASCADE
forbill_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中使用索引
-
阅读手册中关于索引的章节 。