JKo*_*sin 6 mysql database-design referential-integrity
为这个晦涩的问题道歉,用一个具体的例子可能更有意义:
在我的应用程序中,我可能会创建一个包含一组项目的投资组合。但是,我也可以添加一个嵌套在投资组合(然后可能包含项目)中的“子投资组合”。目前,我通过以下方式绘制了我的架构:
+------------+ +-----------------------+ +------------+
| Portfolios | | Portfolio_project_map | | Projects |
+------------+ +-----------------------+ +------------+
| port_id | | parent_id | | proj_id |
+------------+ +-----------------------+ +------------+
| | | child_id | | |
+-----------------------+
| child_is_portfolio |
+-----------------------+
Run Code Online (Sandbox Code Playgroud)
这看起来非常难看。在关系数据库中建模这种关系的正确方法是什么?还是真的没有,这样的细节应该在业务逻辑中严格执行?它甚至有可能对指定的外键约束Portfolio_project_map
,使得child_id
引用要么port_id
还是proj_id
?
另外,这样的问题到底叫什么?我发现在几个地方引用了“一对一”,但我有一种不完全正确的感觉?
如果关系是严格分层的(即一个项目组合可以有多个子项目组合或项目,但一个项目不能出现在多个子项目组合中),那么您可以使用子类型模式对项目组合和项目进行建模,例如
-- === PortfolioItem table ===========================================
-- Supertype table that records the relationships between portfolio
-- items.
create table PortfolioItem (
PortfolioItemID int not null
,PortfolioItemRef varchar (20) not null
,PortfolioItemType varchar (10) not null -- either 'PROJ' or 'PORT'
,ParentItemID int -- Null for root
)
go
alter table PortFolioItem
add constraint PK_PortfolioItem
primary key nonclustered (PortfolioItemID)
go
alter table PortfolioItem
add constraint UQ_PortfolioItem
unique nonclustered (PortfolioItemRef, PortfolioItemType)
go
-- Parent-child relationship
--
alter table PortfolioItem
add constraint FK_PortfolioItem_Parent
foreign key (ParentItemID)
references PortfolioItem
go
-- === PortfolioSubType ===============================================
-- This subclass table joins against the unique identifier but the
-- check constraint restricts it to joining against 'portfolio' nodes
--
create table PortfolioSubType (
PortfolioItemRef varchar (20) not null
,PortfolioItemType varchar (10) not null
-- Portfolio attributes
)
alter table PortfolioSubType
add constraint PK_PortfolioSubType
primary key nonclustered (PortfolioItemRef, PortfolioItemType)
go
-- Cab only join against portfolio parent items
--
alter table PortfolioSubType
add constraint CK_Portfolio_Type
check (PortfolioItemType = 'PORT')
go
alter table PortfolioSubType
add constraint FK_PortfolioSubType_SuperType
foreign key (PortfolioItemRef, PortfolioItemType)
references PortfolioItem (PortfolioItemRef, PortfolioItemType)
go
-- === ProjectSubType =================================================
-- This subclass table has the project specific items and a check
-- constraint that prevents it from joining against parent nodes
-- that represent portfolios
--
create table ProjectSubType (
PortfolioItemRef varchar (20) not null
,PortfolioItemType varchar (10) not null
-- Project attributes
)
alter table ProjectSubType
add constraint PK_ProjectSubType
primary key nonclustered (PortfolioItemRef, PortfolioItemType)
go
-- Check constraint restricts this to projects
--
alter table ProjectSubType
add constraint CK_Portfolio_Type
check (PortfolioItemType = 'PROJ')
go
alter table ProjectSubType
add constraint FK_ProjectSubType_SuperType
foreign key (PortfolioItemRef, PortfolioItemType)
references PortfolioItem (PortfolioItemRef, PortfolioItemType)
go
Run Code Online (Sandbox Code Playgroud)
您可以强制执行完整性规则,以防止项目节点拥有具有类似于以下触发器的子节点:
-- === Trigger to enforce integrity ===================================
-- The trigger prevents project nodes from having children.
--
create trigger ProjectNodeIntegrity
on PortfolioItem
for insert, update
as
if exists
(select 1
from PortfolioItem p_i
join inserted i
on i.ParentItemID = p_i.PortfolioItemID
and p_i.PortfolioItemType = 'PROJ') begin
raiserror ('Only portfolios may have children', 16, 1)
rollback transaction
return
end
go
Run Code Online (Sandbox Code Playgroud)
这将反弹在项目节点下插入子节点的尝试。