如何处理关系型及对象关系型数据库中的多对多关系?
嘿,刚好对这个问题门儿清!在MySQL、MariaDB这类关系型数据库,还有Postgres这种对象关系型数据库里,处理多对多关系确实有两种主流方案,就拿你提到的Product和Bill的场景——一个商品对应多个账单,一个账单包含多个商品,也就是Product(n) ↔ Bill(n)这种典型的多对多关联来说:
处理多对多关系的两种方案
方案1:创建中间关联表(推荐的标准做法)
这是行业内最通用、最符合数据库设计范式的解决方案——通过新建一张关联表(也叫连接表、交叉表),把原本的多对多关系拆解成两个一对多关系:
- Product表 ↔ 关联表:一个商品可以对应多条关联记录
- Bill表 ↔ 关联表:一个账单可以对应多条关联记录
关联表的核心字段是两个外键:分别指向Product表和Bill表的主键,还可以根据业务需求添加额外字段(比如商品在账单里的购买数量、当时的单价等)。
示例SQL代码
-- 先创建Product主表 CREATE TABLE Product ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL ); -- 创建Bill主表 CREATE TABLE Bill ( id INT PRIMARY KEY AUTO_INCREMENT, bill_number VARCHAR(50) NOT NULL UNIQUE, create_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 创建中间关联表 CREATE TABLE Product_Bill ( product_id INT NOT NULL, bill_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, -- 商品在该账单中的数量 price_at_purchase DECIMAL(10,2) NOT NULL, -- 购买时的单价(避免商品调价后影响历史账单) PRIMARY KEY (product_id, bill_id), -- 联合主键,防止同一商品重复关联到同一账单 FOREIGN KEY (product_id) REFERENCES Product(id) ON DELETE CASCADE, FOREIGN KEY (bill_id) REFERENCES Bill(id) ON DELETE CASCADE );
这里的ON DELETE CASCADE是可选的,作用是当主表(比如Product)的记录被删除时,关联表中对应的记录也会自动删除,根据你的业务需求调整即可。
方案2:在单表中存储关联ID集合(不推荐,仅适用于极简场景)
这种方案是把关联的ID集合存在某一张主表的字段里,比如在Bill表加一个product_ids字段,用逗号分隔字符串(比如"1,3,5"),或者用Postgres支持的数组类型(比如INT[])。
示例SQL代码(Postgres为例)
CREATE TABLE Bill ( id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, bill_number VARCHAR(50) NOT NULL UNIQUE, create_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, product_ids INT[] -- Postgres原生数组类型 );
⚠️ 注意:这种方案的弊端非常明显——无法高效执行JOIN查询、难以维护关联关系、无法存储关联的额外业务数据(比如数量、单价),而且不符合数据库设计范式,只适合完全不需要复杂查询的极简场景,99%的业务场景都不建议用。
内容的提问来源于stack exchange,提问作者qangdev
相关产品推荐
相关产品推荐

