Самостоятельная ссылка на ограничения внешнего ключа и удаление - PullRequest
7 голосов
/ 24 марта 2011

Каков рекомендуемый способ обработки самообращающихся ограничений на внешние ключи в SQL-сервере?

Таблица-модель:

enter image description here

fiData ссылается на предыдущую запись в tabData. Если я удаляю запись, на которую ссылается fiData, база данных выдает исключение:

"Оператор DELETE конфликтует с ОДНОЙ ТАБЛИЦЕЙ ограничение "FK_tabDataPrev_tabDataNext". Конфликт произошел в база данных "MyDataBase", таблица "dbo.tabData", столбец "fiData" "

, если Enforce Foreignkey Constraint установлено на «Да».

Мне не нужно каскадно удалять записи, на которые есть ссылки, но мне нужно было бы установить fiData=NULL там, где на них ссылаются. Моя идея состоит в том, чтобы установить Enforce Foreignkey Constraint на «Нет» и создать триггер удаления. Это рекомендуется или есть лучшие способы?

Спасибо.

Ответы [ 3 ]

7 голосов
/ 24 марта 2011

В отличие от Andomar, я был бы счастлив использовать триггер - но я бы не убрал проверку ограничений.Если вы реализуете его как триггер instead of, вы можете сбросить другие строки до нуля, прежде чем выполнять фактическое удаление:

CREATE TRIGGER T_tabData_D
on tabData
instead of delete
as
    set nocount on
    update tabData set fiData = null where fiData in (select idData from deleted)
    delete from tabData where idData in (select idData from deleted)

Это коротко, кратко, не нужно, если SQL Server можетобрабатывать каскады внешних ключей в той же таблице (в других СУБД 'вы можете просто указать ON DELETE SET NULL для ограничения внешнего ключа, YMMV).

2 голосов
/ 24 марта 2011

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

В вашем примере установка принудительного ограничения на «Нет» означает, что вы можете добавить несуществующий идентификатор.Кроме того, оптимизатор запросов будет менее эффективен, поскольку не может предположить, что ключ действителен.

Рассмотрите возможность создания хранимой процедуры:

create procedure dbo.NukeTabData(
    @idData int)
as
begin transaction
update tabData set fiData = null where fiData = @idData
delete from tabData where idData = @idData
commit transaction
go
0 голосов
/ 05 октября 2012

Очень поздно, чтобы ответить.

Но для кого-то, кто ищет, как я.

и хочет cascade

вот очень хорошее объяснение

http://devio.wordpress.com/2008/05/23/recursive-delete-in-sql-server/

Проблема Хотя вы можете определить внешний ключ с помощью CASCADE DELETE в SQL Server, рекурсивное каскадное удаление не поддерживается (т.е. каскадное удаление в той же таблице).

Если вы создаете триггер INSTEAD OF DELETE, этот триггер срабатывает только для первого оператора DELETE и не запускается для записей, рекурсивно удаленных из этого триггера.

Это поведение задокументировано в MSDN для SQL Server 2000 и SQLServer 2005.

Решение Предположим, у вас есть таблица, определенная так:

CREATE TABLE MyTable (
    OID    INT,        -- primary key
    OID_Parent INT,    -- recursion
    ... other columns
)

, тогда триггер удаления выглядит следующим образом:

CREATE TRIGGER del_MyTable ON MyTable INSTEAD OF DELETE
AS
    CREATE TABLE #Table(
        OID    INT
    )
INSERT INTO #Table (OID)
SELECT  OID
FROM    deleted

DECLARE @c INT
SET @c = 0

WHILE @c <> (SELECT COUNT(OID) FROM #Table) BEGIN
    SELECT @c = COUNT(OID) FROM #Table

    INSERT INTO #Table (OID)
    SELECT  MyTable.OID
    FROM    MyTable
    LEFT OUTER JOIN #Table ON MyTable.OID = #Table.OID
    WHERE   MyTable.OID_Parent IN (SELECT OID FROM #Table)
    AND     #Table.OID IS NULL
END

DELETE  MyTable
FROM    MyTable
INNER JOIN #Table ON MyTable.OID = #Table.OID

GO
...