繁体   English   中英

如何为ParentId / HierarchyId编写多行约束

[英]How to write multi-line constraint for ParentId/HierarchyId

我有一个名为Items的表,具有以下三个字段:

  • ID(int)
  • 的ParentId(INT)
  • 节点编号(HierarchyId)

     Id NodeId ParentId 2 / NULL 3 /120/1/ 1520 4 /1/ 2 

ParentIdNodeId基本上都是重复的,因为它们都指向父对象。 将来, ParentId将被ParentIdHierarchyId

但是,与此同时,我需要确保它们保持同步,因此我需要编写一个约束来确保这一点:

declare @id int = 4

DECLARE @result int = 0, @parentNodeId1 HierarchyId,@parentNodeId2 HierarchyId, @parentId int; 


Select @parentNodeId1 = NodeId.GetAncestor(1), @parentId = parentId from Items where id = @id;

Select @parentNodeId2 = NodeId from Items where Id = @parentId;

if @parentNodeId1 = @parentNodeId2
Select @result = 1;

如何写为约束?

更新

我按照建议尝试了Damiens解决方案。 它可以对现有数据强制执行多列完整性。 但是,NodeId可以在层次结构中从左到右更改。 例如, /1/的NodeId可以更改为/3/ 它的父级没有更改(父级是/ ),因此实际上外键尚未经过验证。 这适用于没有孩子的物品。

但是,如果项目具有子项,并且NodeId已更改,则数据库引擎会将其视为违反外键,并引发错误。

看到此数据库提琴: https ://dbfiddle.uk/ ? rdbms = sqlserver_2017 & fiddle = 44637c2bbed80b8db3f305933798cdad

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[People](
    [Id] [int] NOT NULL,
    [ParentId] [int] NULL,
    [NodeId] [hierarchyid] NOT NULL,
    [_ParentNodeId]  AS ([NodeId].[GetAncestor]((1))) PERSISTED,
 CONSTRAINT [PK_People] PRIMARY KEY CLUSTERED 
(
    [Id] ASC,
    [NodeId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
INSERT [dbo].[People] ([Id], [ParentId], [NodeId]) VALUES (1, NULL, N'/')
GO
INSERT [dbo].[People] ([Id], [ParentId], [NodeId]) VALUES (2, 1, N'/2/')
GO
INSERT [dbo].[People] ([Id], [ParentId], [NodeId]) VALUES (3, 1, N'/3/')
GO
INSERT [dbo].[People] ([Id], [ParentId], [NodeId]) VALUES (4, 2, N'/2/1/')
GO
INSERT [dbo].[People] ([Id], [ParentId], [NodeId]) VALUES (5, 4, N'/2/1/1.1/')
GO
ALTER TABLE [dbo].[People]  WITH CHECK ADD  CONSTRAINT [FK_People_Parent] FOREIGN KEY([ParentId], [_ParentNodeId])
REFERENCES [dbo].[People] ([Id], [NodeId])
GO
ALTER TABLE [dbo].[People] CHECK CONSTRAINT [FK_People_Parent]
GO

Update People set NodeId = N'/2/1/2/' where Id = 5--Works


Update People set NodeId = N'/2/2/' where Id = 4 --Error

如果我们可以添加另一列,则外键约束可以强制执行此操作:

create table T(
    Id int not null,
    NodeId hierarchyid,
    ParentId int,
    _ParentNodeId as NodeId.GetAncestor(1) persisted,
    constraint PK_T primary key (Id,NodeId),
    constraint FK_T_Parent foreign key (ParentId,_ParentNodeId) references T (Id,NodeId)
)

(我将PK用作FK目标,但是,例如,如果Id是当前PK,而您想保留该唯一约束,则可以使用任何唯一约束)

我在新列名称前添加了_ 这是我的约定,“ 出于增强完整性的目的而添加此列。我不希望其他人看到它”。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM