多对多关系约束现有方案弊端及架构优化问询
问题描述
现有如下表结构及关系:
CREATE TABLE DocumentType ( ID uniqueidentifier primary key, ... ) GO CREATE TABLE LinkedDoc ( ID uniqueidentifier ROWGUIDCOL NOT NULL, DocumentTypeID uniqueidentifier NOT NULL, ..., CONSTRAINT [PK_LinkedDoc] PRIMARY KEY CLUSTERED (ID, DocumentTypeID) ) GO ALTER TABLE LinkedDoc ADD CONSTRAINT FK_DocumentType_LinkedDoc FOREIGN KEY(DocumentTypeID) REFERENCES DocumentType (ID) GO CREATE TABLE SomeOtherTable ( ID uniqueidentifier primary key, ... ) CREATE TABLE ManyToMany ( ID uniqueidentifier primary key, LinkedDocID uniqueidentifier, DocumentTypeID uniqueidentifier, SomeOtherTableID uniqueidentifier, CONSTRAINT IX_ManyToMany UNIQUE (DocumentTypeID, SomeOtherTableID) ) GO ALTER TABLE ManyToMany WITH CHECK ADD CONSTRAINT FK_SomeOtherTable_ManyToMany FOREIGN KEY (SomeOtherTableID) REFERENCES SomeOtherTable (ID) GO ALTER TABLE ManyToMany WITH CHECK ADD CONSTRAINT FK_LinkedDoc_ManyToMany FOREIGN KEY (LinkedDocID, DocumentTypeID) REFERENCES LinkedDoc (ID, DocumentTypeID) GO
核心需求:SomeOtherTable中的每个唯一实体,无法关联同一文档类型的多个LinkedDoc。目前通过IX_ManyToMany唯一约束实现,但存在两个明显问题:
IX_ManyToMany对表主键存在部分依赖;LinkedDoc的主键为(ID, DocumentTypeID),但ID本身是全局唯一的ROWGUIDCOL,导致主键非最小化,存在冗余。
希望通过重构表结构优化方案,同时避免使用数据库触发器和函数,倾向于将业务逻辑放在便于版本控制和维护的API层。
优化方案
1. 先简化LinkedDoc的主键结构
既然LinkedDoc.ID是全局唯一的ROWGUIDCOL,完全可以单独作为主键,无需将DocumentTypeID加入主键组合。调整后的表结构:
CREATE TABLE LinkedDoc ( ID uniqueidentifier ROWGUIDCOL NOT NULL PRIMARY KEY CLUSTERED, DocumentTypeID uniqueidentifier NOT NULL, ... ) GO ALTER TABLE LinkedDoc ADD CONSTRAINT FK_DocumentType_LinkedDoc FOREIGN KEY(DocumentTypeID) REFERENCES DocumentType (ID) GO
此调整既保留了ROWGUIDCOL的特性,又实现了主键最小化,消除了冗余。
2. 基于视图实现无冗余的唯一约束
简化LinkedDoc主键后,ManyToMany表可去掉冗余的DocumentTypeID字段,通过关联LinkedDoc获取文档类型信息。同时可通过带唯一聚集索引的视图实现需求,全程无需触发器或函数:
步骤1:创建关联视图
CREATE VIEW vw_ManyToMany_DocumentType AS SELECT mtm.SomeOtherTableID, ld.DocumentTypeID FROM ManyToMany mtm JOIN LinkedDoc ld ON mtm.LinkedDocID = ld.ID
步骤2:给视图添加唯一聚集索引
CREATE UNIQUE CLUSTERED INDEX UQ_SomeOtherTable_DocumentType ON vw_ManyToMany_DocumentType (SomeOtherTableID, DocumentTypeID)
该索引会强制SomeOtherTableID与DocumentTypeID的组合唯一,直接满足“同一实体不能关联同一文档类型多个LinkedDoc”的需求,同时避免了原方案中ManyToMany表冗余存储DocumentTypeID的问题。
3. 备选方案:API层全权控制约束
若完全不想在数据库层面做约束,可选择:
- 删除
ManyToMany表中所有相关约束; - 在API写入数据前,先查询当前
SomeOtherTableID关联的所有LinkedDoc对应的DocumentTypeID,检查是否存在重复; - 若存在重复则返回错误,否则执行插入操作。
此方式将业务逻辑完全放在API层,便于版本控制,但需注意并发场景下的竞态问题,可通过API层加锁或数据库乐观锁解决。
内容的提问来源于stack exchange,提问作者Reece Deyoung
相关产品推荐
相关产品推荐

