2012-03-20 161 views
35

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

我的例子:

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

查询不建立m-t-m关系 - 表结构。 – 2012-03-20 15:30:16

+0

我没有很好地表达自己,我该如何定义我的表格结构? – 2012-03-20 15:32:04

+1

从帐单表中删除产品,创建一个名为“bill_products”的新表,其中有两个字段:一个指向产品,一个指向帐单。使这两个字段成为这个新表的主键。 – 2012-03-20 15:34:56

回答

164

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标识符。虽然这是可能的,但它是不好的风格,并导致错误和错误消息混淆。使用​​。如果可以的话,切勿使用reserved words并避免使用双引号混合大小写标识符。

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

  • price数据类型numeric来存储小数精确地输入(任意精度类型,而不是浮点型)。如果你专门处理整个号码,那就做integer。例如,您可以将价格节省作为分销商

  • amount"Products"在您的问题)进入链接表bill_product并且类型numeric以及。如果你专门处理整数,integer

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

  • 在这个基本的例子中的所有列最终是NOT NULL,所以NULL值是不允许的。 (是的,全部列 - 主键中使用的列自动定义为UNIQUE NOT NULL)。这是因为NULL值在任何列中都没有意义。它使初学者的生活更轻松。但你不会轻易离开,无论如何你需要了解NULL handling。附加列可能允许NULL价值,功能和连接可以查询等

  • 阅读CREATE TABLE in the manual章介绍NULL值。

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

  • 阅读chapter on indexes in the manual

+22

主席先生,为了提高答案的质量,即使在几周后,你也绝对不会回来多次。我感谢你成为社区的忠诚贡献者! – 2014-12-05 12:11:53

+3

@RaduGheorghiu:谢谢。我多次提到这个答案,所以我一直在提炼它。 – 2014-12-05 12:33:37

+0

如何为映射表“bill_product”创建索引?通常它看起来应该是这样的:'CREATE INDEX idx_bill_product_id ON booked_rates(bill_id,product_id)'。这是正确的吗? – codyLine 2015-01-05 23:46:04

相关问题