Самореферентное ограничение в MS SQL - PullRequest
11 голосов
/ 09 февраля 2009

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

"Введение ограничения FOREIGN KEY может вызвать циклы или несколько каскадов пути. Укажите НА УДАЛИТЬ НЕТ ДЕЙСТВИЙ или ОБНОВЛЕНИЕ НЕТ ДЕЙСТВИЙ, или изменить другие Ограничения FOREIGN KEY. "

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

Ответы [ 3 ]

9 голосов
/ 09 февраля 2009

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

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

4 голосов
/ 16 августа 2016

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

Это невозможно. Вы можете решить это с 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; 
0 голосов
/ 07 ноября 2012
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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...