2010-08-09 20 views
5

Ich muss ein Lösch-Skript schreiben, um Zeilen aus einer Datenbanktabelle zu löschen. Die Tabelle hat jedoch viele untergeordnete Tabellen (Fremdschlüssel) und diese untergeordneten Tabellen haben auch untergeordnete Tabellen.Wie bekomme ich eine Liste von Kindtabellen für eine Datenbanktabelle?

Es gibt Fremdschlüssel für alle Beziehungen, und ich möchte diese Informationen verwenden, um die Liste der Tabellen zu erhalten, in denen ich löschen muss, in der richtigen Reihenfolge (Tabellen zuerst und dann die Abhängigkeitsgrafik).

Wie kann ich die Liste der untergeordneten Tabellen für eine bestimmte Tabelle in der richtigen Reihenfolge abrufen?

Antwort

5

versuchen Sie dies auf Ihrer Datenbank, dieses Skript gibt Ihnen nur die Grafik für eine Tabelle zu einer Zeit. Ich nehme an, Sie haben eine Mitarbeiter-Tabelle, aber Sie müssten Zeile 2 ändern, um eine bestimmte Tabelle Ihrer Datenbank zu überprüfen:

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

Das ist genau das, was ich brauchte. – Sylvain

1

Es gibt keine einfache generische Antwort dafür, da Tabellen rekursiv von anderen Tabellen einschließlich Selbstbeziehungen usw. abhängen können. Ihr Ergebnis könnte mehr als ein einfacher Baum sein.

Der beste Weg sollte von Ihrem DB-Modell abhängen: Wenn Sie Baumtabellen verbunden haben, dann löschen Sie Ihre Daten aus der dritten Tabelle zuerst, dann als zweite, als dritte.

... oder Einschränkungen deaktivieren, Daten löschen, Einschränkungen aktivieren.

... oder ändern Sie die Fremdschlüssel zu DELETE CASCADE.

Es hängt von Ihrem Datenmodell ab.

+0

löschen Kaskade, in der Regel eine schlechte Sache! Kann die Leistung abbrechen und löscht, wenn die Existenz von Kinddatensätzen ein Show-Stopper sein sollte. – HLGEM

+0

Konnte nicht mehr zustimmen. Wenn jedoch die gesamte Datenbank oder die zugehörige Tabelle sehr klein und "unbedeutend" ist, kann sie die Entwicklung und das Testen beschleunigen. – dmajkic

1

This article gibt eine gute Vorstellung davon, wie Sie tun, was Sie fragen.

EDIT:

  1. Machen Sie das Skript Schema bewusst
  2. korrigieren Fehler unter
  3. in den Kommentare bemerkt

Nicht sicher: Ich habe die ursprüngliche Abfrage in dem Link zu bestimmten modifizierten Warum macht der Editor so einen schlechten Job beim Formatieren des Codeblocks?

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 - Die Lösung funktioniert in einer Testdatenbank, die ich erstellt habe. Es funktioniert jedoch nicht mit meiner realen Datenbank. Ich bekomme diesen Fehler: Die maximale Rekursion 100 wurde vor Abschluss der Anweisung erschöpft. Ich habe versucht, die maximale Rekursion zu erhöhen und habe das gleiche Ergebnis erhalten. Ich vermute, dass es einen Randfall gibt, der nicht von diesem Code abgedeckt wird und der eine unendliche Rekursion verursacht. – Sylvain

+0

Für dieses Problem hängen Sie diese am Ende der Abfrage an: Option (maxrecursion 0). (Standardmäßig wird die maximal zulässige Anzahl festgelegt. Sie können diese Zahl auch auf eine ganze Zahl> 0 setzen, wenn Sie sie auf etwas anderes als 100 beschränken möchten.) –

+0

Ich habe keine Blatttabellen mit mehr als 10 Ebenen. Es ist ein Fehler in der SQL, der eine unendliche Rekursion verursacht. – Sylvain

Verwandte Themen