Ограничение привязки в 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