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

Rad*_*hiu 68 sql database postgresql database-design many-to-many

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

我的例子:

Product(name, price);
Bill(name, date, Products);
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 243

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
);
Run Code Online (Sandbox Code Playgroud)

我做了一些调整:

  • N:M关系通常由一个单独的表来实现- bill_product在这种情况下.

  • 我添加了serial列作为代理主键.我强烈建议,因为产品的名称并不是唯一的.此外,强制唯一性和引用外键中的列integer比使用存储为text或的字符串便宜得多varchar.
    在Postgres 10或更高版本中,请考虑使用IDENTITY.细节:

  • 不要使用基本数据类型的名称,date标识符.虽然这是可能的,但它是糟糕的风格,并导致混淆错误和错误消息.使用合法,小写,不带引号的标识符.如果可以,切勿使用保留字并避免使用双引号混合大小写标识符.

  • name不是一个好名字.我改名为name表列productproduct.这是一个更好的命名约定.否则,当您在查询中连接几个表时 - 您在关系数据库中执行了很多操作 - 最终会得到多个name已命名的列,并且必须使用列别名来排除混乱.那没用.另一种广泛的反模式id就像列名一样.
    我不确定a的名字是什么bill.也许在这种情况下bill_id可以是名称.

  • price是一种数据类型, numeric用于精确存储输入的分数(任意精度类型而不是浮点类型).如果你专门处理整数,那就做吧integer.例如,您可以将价格保存为美分.

  • amount("Products"你的问题)进入链接表bill_product是类型的numeric为好.再次,integer如果你专门处理整数.

  • 你看到外键bill_product吗?我创建了两个级联更改(ON UPDATE CASCADE):如果a product_idbill_id应该更改,则更改将级联到所有依赖条目,bill_product并且没有任何中断.
    我也用ON DELETE CASCADEbill_id:如果你删除了一项法案,细节用它删除.
    产品不是这样:您不想删除帐单中使用的产品.如果你尝试这个,Postgres会抛出一个错误.您可以添加另一列product来标记过时的行.

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

  • 请阅读CREATE TABLE手册中的章节.

  • 主键通过键列上的唯一索引实现,这使得PK列上的条件快速查询.但是,键列的顺序与多列键相关.由于PK 示例在我的示例中bill_product已启用(bill_id, product_id),因此您可能希望仅添加另一个索引,product_id或者(product_id, bill_id)如果您有查询给定a product_id和no bill_id.细节:

  • 阅读手册中有关索引章节.

  • @codyLine:该索引是由 PK 自动创建的。 (2认同)
  • @ChristianB.Almeida:这在很多情况下都很有用,是的。我添加了一些关于索引的内容。 (2认同)