7

SQL Server에서 자체 참조 외부 제약 조건을 처리하는 데 권장되는 방법은 무엇입니까?자체 참조 외래 키 제약 조건 및 삭제

테이블 모델 :

enter image description here

fiData 참조 tabData에서 이전 기록. "MYDATABASE, 테이블을". 충돌이 데이터베이스에 발생했습니다 "FK_tabDataPrev_tabDataNext

"같은 테이블 참조 제약 조건과 충돌 DELETE 문 "내가 fiData 참조하는 레코드를 삭제하면 데이터베이스에서 예외가 발생합니다 "dbo.tabData", 열 'fiData' "

Enforce Foreignkey Constraint이"예 "로 설정된 경우.

참조되는 레코드를 계단식으로 삭제할 필요는 없지만 참조되는 곳은 fiData=NULL으로 설정해야합니다. 내 생각은 Enforce Foreignkey Constraint을 "No"로 설정하고 삭제 트리거를 생성하는 것입니다. 이 방법이 더 좋습니까?

감사합니다.

답변

7

Andomar 달리, 나는 트리거를 사용하여 드리겠습니다 -하지만 제약 조건 검사를 제거 할 것 :

대신 저장 프로 시저를 작성하는 것이 좋습니다. 당신이 instead of 트리거로 구현하는 경우, 당신은 수행하기 전에 null로 다른 행을 재설정 할 수 있습니다 실제 삭제 : SQL Server가 외래 키 폭포를 처리 할 수있는 경우

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) 

그것은 짧다, 그것은 간결, 그것은 필요하지 않을 것입니다 (다른 RDBMS에서는 외래 키 제약 조건 인 YMMV에 ON DELETE SET NULL을 지정할 수 있습니다).

+1

감사합니다. 그러나 트리거 만들기에 대한 예외가 생깁니다. "DELETE/UPDATE 트리거 대신 INSTEAD/UPDATE 트리거는 정의 된 DELETE/UPDATE 동작에 대해 계단식으로 외래 키가있는 테이블에서 정의 할 수 없습니다." –

+0

나는이 말을 너무 늦게 언급하고 있지만 나 같은 사람을 찾고있다. 하나의 항목 만 삭제합니다. 재귀 적으로 캐스케이드되지는 않습니다. – Arif

+0

@Arif - "ON DELETE CASCADE"가 아니라 "ON DELETE SET NULL"이었습니다. 전자는 재발 할 필요가 없습니다. "ON DELETE CASCADE"에 대해서는 먼저 모든 ID 값에 대해 클로저를 계산 한 다음 삭제를 수행하는 CTE를 권합니다. –

2

트리거는 암시적인 복잡성을 추가합니다. 트리거가있는 데이터베이스에서 SQL 문이 무엇을 하는지를 알면 알 수 없습니다. 제 경험상 트리거는 예외가 아닌 나쁜 생각입니다.

예에서 강제 제한을 "아니요"로 설정하면 존재하지 않는 ID를 추가 할 수 있습니다. 그리고 쿼리 최적화 프로그램은 키가 유효하다고 가정 할 수 없기 때문에 효과가 떨어집니다.

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 
+3

이제는 호출 코드에'exec NukeTabData (ID)'가 있으며 프로 시저의 내용을 검토하지 않고 어떤 코드인지 알지 못합니다. 방아쇠가 아닌데 왜 괜찮습니까? –

+2

@Damien_The_Unbeliever : 스토어드 프로 시저는 명시 적입니다. 세부 사항에 관심이 있다면 어디에 표시해야하는지 알려줍니다. 트리거는 함축적입니다 :'delete from tabData where idData = 42'에서 아무것도 실행되지 않을 추가 업데이트가 있음을 암시합니다. 예를 들어, delete를 실행 한 후에 트리거를 사용하면'@@ rowcount'가 1이 아닌 4가 될 수 있습니다.이 경우 버그가 생기기 쉽습니다. 특히 코드 기반을 처음 접하는 개발자의 경우 더욱 그렇습니다. – Andomar

+0

@@ ROWCOUNT는 범위 기반입니다. 트리거 내의 모든 활동의 영향을받지 않습니다. 아마도 @@ IDENTITY에 대해 생각하고있을 것입니다. 그러나 대부분의 사람들은 요즘은이를 피하는 법을 알고 있습니다. –

0

답변이 매우 늦었습니다.

하지만 나 같은 사람을 찾고 있습니다. cascade 여기

하고 싶은 아주 좋은 설명

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

당신이 CASCADE와 외래 키가 SQL 서버에서 삭제 정의 할 수 있지만, 재귀 계단식 삭제가 지원되지 않습니다 문제 (이다 ie 동일한 테이블에 계단식 삭제).

INSTEAD OF DELETE 트리거를 만들면이 트리거는 첫 번째 DELETE 문에 대해서만 실행되며이 트리거에서 재귀 적으로 삭제 된 레코드에는 실행되지 않습니다.

이 문제는 2005 년

솔루션 이 같은 정의 된 테이블이 있다고 가정 SQL Server 2000 및 SQL Server에 대한 MSDN에 설명되어 있습니다 :

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 
+0

이것은 ON DELETE CASCADE에 대한 좋은 대답입니다. 그러나 그 질문은 ON DELETE SET NULL에 관한 것입니다. – dajoropo

관련 문제