2013-08-20 2 views
0

Я пишу небольшое приложение для управления метаданными для меню. A Menu прилагается к App, а также (необязательно) прикрепляется к другому Menu (поэтому определяет подменю). Итак, проводка идет следующим образом: прикрепите Menu.AppId к App.Id, а затем Menu.ParentId - Menu.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') 

Здесь я только что сказал, что Sub Menu из App # 36 должны находиться под Top Menu из App # 25 (другое приложение).

Есть ли способ я могу определить ограничение, чтобы убедиться, что, когда я вставляю Sub Menu как ребенка Top Menu, приложение должно быть # 25 (Триггеры не вариант)?

(Конечно, я буду управлять этим в пользовательском интерфейсе, но я также ищу способ защитить модель).

Спасибо,

+0

Почему триггеры не являются опцией? – HLGEM

+0

@HLGEM Я ищу встроенную функциональность, предоставляемую РСУБД, которую я мог пропустить. Я хочу узнать все возможные варианты перед использованием триггера. – Jeff

ответ

1

Вы можете использовать ограничение внешнего ключа

Первое ограничение (Id, AppId) быть уникальным (это очевидно, если Id является ПК, но необходимо проверить следующее 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) 

Edit: Если вам необходимо изменить AppId, это муз t сделать сразу для каждого меню в иерархии. Это можно сделать с помощью рекурсивного запроса с использованием 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) 

Я не знаю, что такая структура будет работать для вас, но это был единственный способ, которым я мог бы видеть, чтобы придумать рабочий проверочного ограничения.

Смежные вопросы