2010-08-09 5 views
5

데이터베이스 테이블의 행을 삭제하는 삭제 스크립트를 작성해야합니다. 그러나 테이블에는 많은 자식 테이블 (외래 키)이 있으며 그 자식 테이블에도 하위 테이블이 있습니다.데이터베이스 테이블의 하위 테이블 목록을 가져 오는 방법은 무엇입니까?

모든 관계에 대한 외래 키가 있으며이 정보를 사용하여 삭제해야하는 테이블의 목록을 올바른 순서 (리프 테이블부터 종속성 그래프까지)로 가져오고 싶습니다.

주어진 테이블의 하위 테이블 목록을 올바른 순서로 가져 오는 방법은 무엇입니까?

답변

5

데이터베이스에서이 작업을 시도하면이 스크립트는 한 번에 하나의 테이블에 대한 그래프 만 제공합니다. 나는 당신이 Employee 테이블을 가지고 있다고 가정하지만 데이터베이스의 특정 테이블을 검사하기 위해 2 행을 변경해야 할 것입니다 :

DECLARE @masterTableName varchar(1000) 
SET @masterTableName = 'Employee' 

DECLARE @ScannedTables TABLE(Level int, Name varchar(1000) collate Latin1_General_CI_AS) 

DECLARE @currentTableCount INT 
DECLARE @previousTableCount INT 
DECLARE @level INT 

SET @currentTableCount = 0 
SET @previousTableCount = -1 
SET @level = 0 

INSERT INTO @ScannedTables VALUES (@level, @masterTableName) 

WHILE @previousTableCount <> @currentTableCount 
BEGIN 

    SET @previousTableCount = @currentTableCount 

    INSERT INTO @ScannedTables 

     SELECT DISTINCT 
      @level + 1, TC.Table_Name COLLATE Latin1_General_CI_AS 

     FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC 
     LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC ON TC.Constraint_Name = RC.Constraint_Name 
     LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FTC ON RC.Unique_Constraint_Name = FTC.Constraint_Name 

     WHERE TC.CONSTRAINT_TYPE = 'FOREIGN KEY' 

     AND FTC.TABLE_NAME COLLATE Latin1_General_CI_AS IN (SELECT Name FROM @ScannedTables WHERE Level = @level) 
     AND TC.Table_Name COLLATE Latin1_General_CI_AS NOT IN (SELECT Name FROM @ScannedTables) 

    SET @level = @level + 1 

    SELECT @currentTableCount = COUNT(*) FROM @ScannedTables 
END 

SELECT * FROM @ScannedTables 
+0

이것은 내가 필요한 것입니다. – Sylvain

1

테이블은 자기 관계를 비롯한 다른 테이블에 재귀 적으로 의존 할 수 있으므로 이에 대한 간단한 일반적인 대답은 없습니다. 결과는 단순한 트리 이상일 수 있습니다.

가장 좋은 방법은 DB 모델에 따라 달라집니다. 트리 테이블이 연결된 경우 세 번째 테이블보다 먼저 세 번째 테이블에서 데이터를 삭제하십시오.

... 제약 조건을 해제하거나 비활성화하거나, 데이터를 삭제하거나, 제약 조건을 활성화합니다.

... 외래 키를 DELETE CASCADE으로 변경하십시오.

데이터 모델에 따라 다릅니다.

+0

삭제 캐스케이드, 일반적으로 나쁜 것! 어린이 기록의 존재가 쇼 스토퍼 였을 때 성능을 저하시키고 삭제할 수 있습니다. – HLGEM

+0

더 이상 동의 할 수 없습니다. 그러나 전체 데이터베이스 또는 관련 테이블이 매우 작고 "중요하지 않은"경우 개발 및 테스트 속도를 높일 수 있습니다. – dmajkic

1

This article은 당신이 원하는 것을하는 방법에 대한 좋은 아이디어를 제공합니다.

편집 :

  1. 수정 버그가

확실하지 아래 의견에서 언급 한 인식 스크립트 스키마를 확인하십시오 : 나는에 대한 링크에 주어진 원래 쿼리를 수정 한 왜 편집자가 코드 블록을 형식화하는 일을 저조한가.

with Fkeys as (

    select distinct 

     OnTable  = onTableSchema.name + '.' + OnTable.name 
     ,AgainstTable = againstTableSchema.name + '.' + AgainstTable.name 

    from 

     sysforeignkeys fk 

     inner join sys.objects onTable 
      on fk.fkeyid = onTable.object_id 
     inner join sys.objects againstTable 
      on fk.rkeyid = againstTable.object_id 

     inner join sys.schemas onTableSchema 
      on onTable.schema_id = onTableSchema.schema_id 

     inner join sys.schemas againstTableSchema 
      on againstTable.schema_id = againstTableSchema.schema_id 

    where 1=1 
     AND AgainstTable.TYPE = 'U' 
     AND OnTable.TYPE = 'U' 
     -- ignore self joins; they cause an infinite recursion 
     and onTableSchema.name + '.' + OnTable.name <> againstTableSchema.name + '.' + AgainstTable.name 
    ) 

,MyData as (

    select 
     OnTable = s.name + '.' + o.name 
     ,AgainstTable = FKeys.againstTable 

    from 

     sys.objects o 
      inner join sys.schemas s 
       on o.schema_id = s.schema_id 

     left join FKeys 
      on s.name + '.' + o.name = FKeys.onTable 
     left join Fkeys fk2 
      on s.name + '.' + o.name = fk2.AgainstTable 
       and fk2.OnTable = Fkeys.AgainstTable 

    where 1=1 
     and o.type = 'U' 
     and o.name not like 'sys%' 
     and fk2.OnTable is null 
    ) 

,MyRecursion as (

    -- base case 
    select 
     TableName = OnTable 
     ,Lvl  = 1 
    from 
     MyData 
    where 1=1 
     and AgainstTable is null 

    -- recursive case 
    union all select 
     TableName = OnTable 
     ,Lvl  = r.Lvl + 1 
    from 
     MyData d 
     inner join MyRecursion r 
      on d.AgainstTable = r.TableName 
) 

select 
    Lvl = max(Lvl) 
    ,TableName 
    ,strSql = 'delete from [' + tablename + ']' 
from 
    MyRecursion 
group by 
    TableName 
order by 
    1 desc 
    ,2 desc 
+0

+1 -이 솔루션은 내가 만든 테스트 데이터베이스에서 작동합니다. 그러나, 그것은 내 실제 데이터베이스에서 작동하지 않습니다. 이 오류가 발생합니다. 최대 재귀 100은 명령문 완료 전에 고갈되었습니다. 나는 최대 재귀를 시도하고 같은 결과를 얻었다. 그 코드에 의해 다루어지지 않고 무한 재귀를 일으키는 엣지 케이스가 있다고 생각합니다. – Sylvain

+0

해당 문제의 경우 쿼리 끝에 옵션 (maxrecursion 0)을 추가하십시오. (허용되는 최대 수를 기본값으로하고, 100 이외의 값으로 제한하려면 정수> 0으로 설정할 수도 있습니다.) –

+0

리프 테이블이 10 단계를 넘지 않습니다. 무한 재귀를 일으키는 SQL 버그입니다. – Sylvain

관련 문제