簡體   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