Ограничение привязки в MS SQL

Верно ли, что MS SQL ограничивает ограничения для саморегуляции с опцией ON DELETE CASCADE? У меня есть таблица с отношением parent-child, столбец PARENT_ID - это внешний ключ для ID. Создание его с помощью опции DEL DELETE CASCADE вызывает ошибку

"Представление ограничения FOREIGN KEY может вызвать циклы или несколько каскадов пути. Укажите ВКЛ. УДАЛИТЬ НЕТ ДЕЙСТВИЯ или ON UPDATE NO ACTION или изменить другие FOREIGN KEY."

Я не могу поверить, что мне нужно удалить эту иерархию в рекурсивном режиме. Есть ли проблема, кроме триггеров?

Ответ 1

В этом случае вы не можете настроить ON DELETE CASCADE на таблицу с ограничениями для саморегуляции. Существует потенциал циклических логических задач, поэтому он этого не позволит.

Там хорошая статья здесь - хотя это для версии 8, а не для 9 SQL - хотя применяются те же правила.

Ответ 2

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

Это невозможно. Вы можете решить это с помощью INSTEAD OF TRIGGER

create table locations 
(
    id int identity(1, 1),
    name varchar(255) not null,
    parent_id int,

    constraint pk__locations
        primary key clustered (id)

)
GO

INSERT INTO locations(name,parent_id)  VALUES
 ('world',null)
,('Europe',1)
,('Asia',1)
,('France',2)
,('Paris',4)
,('Lyon',4);
GO

- Этот триггер будет использовать рекурсивный CTE для получения всех идентификаторов, следующих за всеми идентификаторами, которые вы удаляете. Эти идентификаторы удаляются.

CREATE TRIGGER dbo.DeleteCascadeLocations ON locations
INSTEAD OF DELETE 
AS
BEGIN
    WITH recCTE AS
    (
        SELECT id,parent_id
        FROM deleted

        UNION ALL

        SELECT nxt.id,nxt.parent_id
        FROM recCTE AS prv
        INNER JOIN locations AS nxt ON nxt.parent_id=prv.id
    )
    DELETE FROM locations WHERE id IN(SELECT id FROM recCTE);
END
GO

- Протестируйте его здесь, попробуйте с разными идентификаторами. Вы можете попробовать WHERE id IN(4,3) также...

SELECT * FROM locations;

DELETE FROM locations WHERE id=4;

SELECT * FROM locations
GO

- Очистка (обратите внимание на реальные данные!)

if exists(select 1 from INFORMATION_SCHEMA.TABLES where TABLE_NAME='locations')
---DROP TABLE locations; 

Ответ 3

CREATE TRIGGER MyTable_OnDelete ON MyTable
INSTEAD OF DELETE
AS 
BEGIN

  SET NOCOUNT ON;

  DELETE FROM mt
  FROM   deleted AS D
  JOIN   MyTable AS mt
  ON     d.Id = mt.ParentId

  DELETE FROM mt
  FROM   deleted AS D
  JOIN   MyTable AS mt
  ON     d.Id = mt.Id

END