2013-08-20 45 views
0

我正在編寫一個小應用程序來管理菜單的元數據。 A Menu附加到App,並且(可選)附加到另一個Menu(因此定義子菜單)。因此,接線如下所示:附加Menu.AppIdApp.Id,然後Menu.ParentIdMenu.Id以定義子菜單。如何定義2列的遞歸外鍵

但是,這可以讓我插不連貫的數據:

INSERT INTO Menu (Id, ParentId, AppId, Desc) values (1, NULL, 25, 'Top Menu') 
INSERT INTO Menu (Id, ParentId, AppId, Desc) values (2, 1, 36, 'Sub Menu') 

在這裏,我只是說,子菜單應用#36應當根據頂級菜單應用#25 (另一個應用程序)。

有沒有一種方法,我可以定義一個約束,以確保當我插入子菜單頂級菜單的孩子,應用程序必須是#25(觸發器是不是一個選項)? (當然,我將在用戶界面中管理這一點,但我也在尋找一種保護模型的方法)。

謝謝

+0

爲什麼觸發器不是一個選項? – HLGEM

+0

@HLGEM我正在尋找RDBMS提供的內置功能,我可能錯過了。我想在使用觸發器之前瞭解所有可能的選項。 – Jeff

回答

1

您可以使用外鍵約束

首先約束(Id, AppId)是唯一的(很明顯,如果Id是PK,但它需要檢查以下FK約束):

alter table Menu add constraint unique_app unique (Id, AppId) 

然後約束孩子有相同的AppId

alter table Menu add constraint fk_same_parent_app 
foreign key (ParentId, AppId) references Menu(Id, AppId) 

編輯:如果您需要更改的AppId,它每畝一次完成層次結構中的每個菜單。 這可以通過使用CTE的遞歸查詢來完成:

with AMenu(Id, ParentId) 
as (
    -- start with a root 
    select Id, ParentId 
    from Menu where Id = <id_of_a_root> 
union all 
    -- recursively add children 
    select m.Id, m.ParentId 
    from Menu m 
    join Amenu am on m.ParentId = am.Id 
) 
update m 
set AppId = <some_value> 
from Menu m 
join AMenu am 
on m.Id = am.Id 
+0

這看起來不錯,但是一旦我將孩子添加到父母身上,我就不能再更改其AppId(當然這很自然)。任何建議來解決這個問題? – Jeff

+0

添加了更新查詢 – bwt

+0

非常好,謝謝。 – Jeff

0

好吧,我有一個辦法,但它需要你做出的appid允許空值。在這種情況下,appid只會在父級中。

create table #temp (parentid int null, appid int NUll) 
ALTER TABLE #temp 
ADD CONSTRAINT myconstraint CHECK (parentid+appid = Null and (isnull(parentid, 0)+isnull(appid,0) = parentid or isnull(parentid, 0)+isnull(appid,0) = appid)); 

insert #temp 
values(1,null) 
insert #temp 
values(null,1) 
insert #temp 
values(1,1) 

我不知道那種結構會適合你,但這是我能看到想出一個工作檢查約束的唯一方法。