【问题标题】:How to physically represent a parent-child relationship, with a favourite, enforcing constraints?如何用最喜欢的、强制的约束来物理表示父子关系?
【发布时间】:2014-02-28 07:34:51
【问题描述】:

我有一对必须合理频繁发生的逻辑实体关系。

考虑两个实体ParentChild。不出所料,每个Parent 至少有一个或多个Children

到目前为止很明显。

此外,每个Parent 都有一个收藏夹 Child

我有一些限制或要求来告知最佳解决方案,

  • ChildParent 都有一个主键,它是一个身份列。

  • 解决方案必须强制执行完整性(尽可能)。我不希望有没有孩子的父母、没有最喜欢的孩子的父母或孤儿。

  • 我想简单快速地访问父母最喜欢的孩子。

  • 我希望所有孩子(包括收藏夹)都驻留在同一个表中,这样我就可以在没有联合或联接的情况下搜索它们。

  • 我需要能够将数据插入表中。


这是我的起点,fiddle here

CREATE TABLE [Parent]
(
    [Id] INT NOT NULL IDENTITY,
    [FavouriteChildId] INT NOT NULL,
    [Other] NVARCHAR(MAX) NOT NULL,
    CONSTRAINT [PK_Parent] PRIMARY KEY ([Id])
);

CREATE TABLE [Child]
(
    [Id] INT NOT NULL IDENTITY,
    [ParentId] INT NOT NULL,
    [Other] NVARCHAR(MAX) NOT NULL,
    CONSTRAINT [PK_Child] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Child_Parent]
        FOREIGN KEY ([ParentId]) REFERENCES [Parent]([Id])
);

ALTER TABLE [Parent]
    ADD CONSTRAINT [FK_Parent_FavouriteChild]
        FOREIGN KEY ([FavouriteChildId]) REFERENCES [Child]([Id]);

这满足了我的所有要求,除了我不能插入数据。

【问题讨论】:

  • 您无法满足您的要求,因为单个INSERT 语句只能影响单个表并且SQL Server 不支持延迟约束。因此,您遇到了“没有孤儿”和“没有没有孩子的父母”的鸡与蛋问题
  • @Damien_The_Unbeliever,这就是我所在的位置,解决它的最佳妥协是什么?我试图思考其他模式可能会提供一个好的解决方案,显然,能够插入很重要。
  • 我会允许没有孩子的父母存在。没有这种限制,其余的一切都很容易实现。
  • @Damien_The_Unbeliever,这是我上次遇到这种情况时所做的,我需要记忆方面的帮助。如果您愿意,可以将其作为答案。
  • 您是否尝试过 OUTPUT 子句用一个语句插入两个表?像 INSERT INTO Parent ... OUTPUT .. INTO Child ...

标签: sql sql-server database-design sql-server-2012


【解决方案1】:

这就是我要做的。这是对您的限制的轻微放松,但希望足够接近:

CREATE TABLE [Parent]
(
    [Id] INT NOT NULL IDENTITY,
    [Other] NVARCHAR(MAX) NOT NULL,
    CONSTRAINT [PK_Parent] PRIMARY KEY ([Id])
);

CREATE TABLE [Child]
(
    [Id] INT NOT NULL IDENTITY,
    [ParentId] INT NOT NULL,
    IsFavourite bit not null,
    FavouriteMarker as CASE WHEN IsFavourite = 1 THEN 1 ELSE -Id END persisted,
    FavouriteRef as CASE WHEN IsFavourite = 0 THEN 1 END persisted,
    [Other] NVARCHAR(MAX) NOT NULL,
    CONSTRAINT [PK_Child] PRIMARY KEY ([Id]),
    CONSTRAINT [FK_Child_Parent]
        FOREIGN KEY ([ParentId]) REFERENCES [Parent]([Id]),
    CONSTRAINT UQ_Child_Favourite UNIQUE (ParentID,FavouriteMarker),
    CONSTRAINT FK_Child_FavouriteReference FOREIGN KEY (ParentID,FavouriteRef)
        references Child (ParentID,FavouriteMarker)
);

以及一些用于练习这些表格的语句:

insert into Parent (Other) values ('ABC'); --We'll assume this gets assigned ID 1

insert into Child (ParentId,IsFavourite,Other) values (1,0,'def') --Fails - no favourite child yet
insert into Child (ParentId,IsFavourite,Other) values (1,1,'ghi') --Succeeds
insert into Child (ParentId,IsFavourite,Other) values (1,1,'jkl') --Fails - no second favourite
insert into Child (ParentId,IsFavourite,Other) values (1,0,'mno') --Succeeds
insert into Child (ParentId,IsFavourite,Other) values (1,0,'pqr') --Succeeds
update Child set IsFavourite =
        CASE WHEN Other = 'mno' THEN 1 ELSE 0 END
where Other in ('ghi','mno') --Succeeds, favourite changed
delete from Parent where Id = 1 --Fails - no orphans

所以它确实允许没有孩子的父母存在,而唯一没有最喜欢孩子的父母就是那些没有孩子的父母。第一个为特定父级添加子级的INSERT 可能是多行插入,但必须包含一个(并且恰好是一个)标记为收藏的子级。此后,可以更改收藏夹。

【讨论】:

  • 计算列上的约束,我喜欢这个想法,它使插入更容易并保证完整性。
  • @Jodrell - 如果您不希望强制执行“没有孩子,除非至少有一个是最喜欢的”约束,那么您根本不需要计算列 - 您只需应用 @ 987654321@: CREATE UNIQUE INDEX IX_Child_Favorites on Child (ParentID) WHERE IsFavourite = 1 并删除两个计算列。有点庆幸的是,计算列可以同时实现“一个最喜欢的”和“没有孩子,除非一个最喜欢”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-29
  • 1970-01-01
  • 1970-01-01
  • 2019-12-05
  • 2018-10-01
  • 2020-11-04
  • 2020-10-07
相关资源
最近更新 更多