Как я неправильно понимаю рекурсию триггера в TSQL? - PullRequest
3 голосов
/ 12 января 2010

У меня есть следующая таблица:

if object_id(N'dbo.Node') is null
create table dbo.Node
(
    ID bigint identity primary key,
    ParentID bigint null, -- references Node(ID)
    DateCreated datetime not null,
    LastUpdated datetime not null,
    [Name] nvarchar(500) not null,
);

У меня есть этот триггер для каскадного удаления в той же таблице:

create trigger Node_Delete on Node for delete
as
begin
    delete from Node where ParentID in (select id from deleted)
end

Вот мой набор данных:

ID                   ParentID             DateCreated             LastUpdated             Name
534                  514                  2010-01-12 10:15:03.940 2010-01-12 10:15:03.940 Test 1
535                  534                  2010-01-12 10:15:08.563 2010-01-12 10:15:08.563 Test 2
536                  535                  2010-01-12 10:15:12.063 2010-01-12 10:15:12.063 Test 3
537                  536                  2010-01-12 10:15:18.510 2010-01-12 10:15:18.510 Test 4

Теперь я выполняю этот запрос:

delete from Node where ID=534

И это результирующий набор данных:

ID                   ParentID             DateCreated             LastUpdated             Name
536                  535                  2010-01-12 10:15:12.063 2010-01-12 10:15:12.063 Test 3
537                  536                  2010-01-12 10:15:18.510 2010-01-12 10:15:18.510 Test 4

Почему оператор DELETE в триггере не вызывает рекурсивное выполнение триггера, пока не будут удалены все дочерние записи?

РЕДАКТИРОВАТЬ: обратите внимание, что я разместил рабочее решение ниже, но пометил другой ответ как правильный, так как мой вопрос был не "каково решение", а скорее "почему мой метод не работает ».

Ответы [ 5 ]

4 голосов
/ 12 января 2010

Я изменил свой триггер на каскад на неограниченную глубину без включения рекурсии триггера. Вот мое решение:

create trigger Node_Delete on Node instead of delete
as
begin
    create table #del ( id bigint, depth int )
    declare @depth int
    set @depth = 1
    insert into #del select id, @depth from deleted
    while @@rowcount > 0
    begin
        set @depth = @depth + 1
        insert into #del select id, @depth from Node where ParentID in (select id from #del where depth = @depth-1)
    end
    delete from Node where ID in (select id from #del)
end

РЕДАКТИРОВАТЬ: теперь я остановился на еще лучшем решении с использованием общих табличных выражений, как это было предложено в другом ответе Младена Прайдика ниже.

create trigger Node_Delete on Node instead of delete
as
begin
    with nodes
    as
    (
        select n.ID, n.ParentID, 1 as Level
        from Node n where n.ID in (select ID from deleted)
        union all
        select n.ID, n.ParentID, p.Level+1 as Level
        from Node n
        inner join nodes p on p.ID = n.ParentID
    )
    delete from Node where ID in (select ID from nodes);
end
2 голосов
/ 12 января 2010

Включить вложенные триггеры (или установить для параметра Allow Triggers to Fire Others значение True, в зависимости от вашей версии sql)

снимок экрана свойств сервера sql http://img34.imageshack.us/img34/5259/capturehjf.png

2 голосов
/ 12 января 2010

Вы должны включить рекурсивные триггеры для базы данных. Обратите внимание, что вы можете получить только 32 уровня глубже.

ALTER DATABASE databasename
SET RECURSIVE_TRIGGERS ON | OFF
1 голос
/ 12 января 2010

Триггер FOR DELETE срабатывает после операции удаления. Таким образом, любые дочерние записи нарушают ограничение FK, делая невозможным удаление.

Вы можете решить эту проблему, написав триггер INSTEAD OF DELETE , как описано в моем блоге.

1 голос
/ 12 января 2010

, поскольку в удаленной псевдотаблице есть только одна строка с идентификатором 534.

Вы можете добавить каскадное удаление в отношениях родитель-ребенок
или используйте CTE, чтобы удалить все.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...