Wie schneidet man mit TSQL alle Tabellen in einer Datenbank ab?

204

Ich habe eine Testumgebung für eine Datenbank, die ich zu Beginn eines Testzyklus mit neuen Daten neu laden möchte. Ich bin nicht daran interessiert, die gesamte Datenbank neu zu erstellen - einfach die Daten "neu einzustellen".

Was ist der beste Weg, um alle Daten mit TSQL aus allen Tabellen zu entfernen? Gibt es gespeicherte Systemprozeduren, Ansichten usw., die verwendet werden können? Ich möchte nicht manuell abgeschnittene Tabellenanweisungen für jede Tabelle erstellen und verwalten. Ich würde es vorziehen, wenn sie dynamisch ist.

Strahl
quelle

Antworten:

188

Für SQL 2005

EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'

Paar weitere Links für 2000 und 2005/2008 ..

Gulzar Nazim
quelle
62
Sie können keine Tabellen mit Fremdschlüsseln abschneiden. Dies funktioniert daher nur, wenn zwischen den Tabellen keine Fremdschlüsseleinschränkungen bestehen (oder diese deaktiviert wurden).
Marcj
1
Ich dachte, da er speziell nach dem Abschneiden von Tabellen gefragt hat, hat er das Problem bereits mit Fremdschlüsseln gelöst.
Gulzar Nazim
@ gulzar- irgendwie habe ich eine separate Frage zum Umgang mit den FKs gestellt, aber Ihre Antwort steht für sich allein.
Ray
11
@ Sam: Nein, das wird es nicht! Die Daten in den Tabellen sind irrelevant. Solange es eine Fremdschlüsseleinschränkung gibt, die auf die Tabelle verweist (auch eine deaktivierte), können Sie sie nicht abschneiden.
TToni
3
'EXEC sp_MSForEachTable' DROP TABLE? ' Funktioniert auch
super
418

Beim Löschen von Daten aus Tabellen mit Fremdschlüsselbeziehungen - was im Grunde bei jeder ordnungsgemäß gestalteten Datenbank der Fall ist - können wir alle Einschränkungen deaktivieren, alle Daten löschen und dann die Einschränkungen wieder aktivieren

-- disable all constraints
EXEC sp_MSForEachTable "ALTER TABLE ? NOCHECK CONSTRAINT all"

-- delete data in all tables
EXEC sp_MSForEachTable "DELETE FROM ?"

-- enable all constraints
exec sp_MSForEachTable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

Weitere Informationen zum Deaktivieren von Einschränkungen und Triggern finden Sie hier

Wenn einige der Tabellen Identitätsspalten haben, möchten wir sie möglicherweise neu setzen

EXEC sp_MSForEachTable "DBCC CHECKIDENT ( '?', RESEED, 0)"

Beachten Sie, dass sich das Verhalten von RESEED zwischen einer brandneuen Tabelle und einer Tabelle unterscheidet, in die zuvor einige Daten aus BOL eingefügt wurden :

DBCC CHECKIDENT ('Tabellenname', RESEED, newReseedValue)

Der aktuelle Identitätswert wird auf newReseedValue gesetzt. Wenn seit ihrer Erstellung keine Zeilen in die Tabelle eingefügt wurden, verwendet die erste nach dem Ausführen von DBCC CHECKIDENT eingefügte Zeile newReseedValue als Identität. Andernfalls wird in der nächsten eingefügten Zeile newReseedValue + 1 verwendet. Wenn der Wert von newReseedValue kleiner als der Maximalwert in der Identitätsspalte ist, wird bei nachfolgenden Verweisen auf die Tabelle die Fehlermeldung 2627 generiert.

Dank an Robert für den Hinweis, dass das Deaktivieren von Einschränkungen das Abschneiden nicht zulässt, müssten die Einschränkungen gelöscht und dann neu erstellt werden

kristof
quelle
33
Das Deaktivieren von Einschränkungen ermöglicht NICHT das Abschneiden von Tabellen, auf die durch eine FOREIGN KEY-Einschränkung verwiesen wird. Die FK-Einschränkung muss entfernt werden. Bitte antworten Sie, wenn ich mich irre, aber ich habe keine Möglichkeit gefunden, sie fallen zu lassen.
Robert Claypool
1
Nur ein Tippfehler "Table" -Schlüsselwort sollte in dieser Anweisung nicht vorhanden sein. EXEC sp_MSForEachTable "DELETE FROM TABLE?" Die korrekte Version sollte lauten: EXEC sp_MSForEachTable "DELETE FROM?"
Raghav
4
Wenn Sie 2008 oder neueres SSMS verwenden, möchten Sie es wahrscheinlich SET ROWCOUNT 0am Anfang Ihres Skripts hinzufügen, da die Aktionen standardmäßig auf 500 Zeilen beschränkt sind! Sie erhalten frustrierende Fehler wie ich, weil nicht alle Daten tatsächlich gelöscht wurden.
Sean Hanley
1
Das hat super geklappt. In meinem Fall musste ich auch EXEC sp_msforeachtable "ALTER TABLE? TRIGGER all deaktivieren" und EXEC sp_msforeachtable "ALTER TABLE? TRIGGER all aktivieren" vor und nach der delete-Anweisung hinzufügen.
RobC
2
Meine Lieblingsantwort. Aber warum schließen Sie (alle, sogar Kommentatoren) wörtliche SQL-Zeichenfolgen in doppelte Anführungszeichen ein?
Bitoolean
57

Hier ist der König der Datenbank-Löschskripte. Es werden alle Tabellen gelöscht und korrekt neu gesetzt:

SET QUOTED_IDENTIFIER ON;
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? NOCHECK CONSTRAINT ALL'  
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? DISABLE TRIGGER ALL'  
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; DELETE FROM ?'  
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? CHECK CONSTRAINT ALL'  
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? ENABLE TRIGGER ALL' 
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON';

IF NOT EXISTS (
    SELECT
        *
    FROM
        SYS.IDENTITY_COLUMNS
        JOIN SYS.TABLES ON SYS.IDENTITY_COLUMNS.Object_ID = SYS.TABLES.Object_ID
    WHERE
        SYS.TABLES.Object_ID = OBJECT_ID('?') AND SYS.IDENTITY_COLUMNS.Last_Value IS NULL
)
AND OBJECTPROPERTY( OBJECT_ID('?'), 'TableHasIdentity' ) = 1

    DBCC CHECKIDENT ('?', RESEED, 0) WITH NO_INFOMSGS;

Viel Spaß, aber sei vorsichtig!

Chris KL
quelle
2
Leider schlägt der obige Befehl fehl, wenn Sie Spalten berechnet haben, da sp_MSforeachtable anscheinend SET QUOTED_IDENTITY OFFin seinem Hauptteil ( Link ) hat. UPDATE: Das Update besteht darin, "SET QUOTED_IDENTIFIERS on" hinzuzufügen. zum Anfang jeder Aussage, die diesen Fehler
auslöst
1
es scheint, dass dies meine Identität nicht neu besiedelt hat
totooooo
48

Der einfachste Weg, dies zu tun, ist zu

  1. Öffnen Sie SQL Management Studio
  2. Navigieren Sie zu Ihrer Datenbank
  3. Klicken Sie mit der rechten Maustaste und wählen Sie Aufgaben-> Skripte erstellen (Bild 1).
  4. Wählen Sie im Bildschirm "Objekte auswählen" die Option "Bestimmte Objekte auswählen" und aktivieren Sie "Tabellen" (Bild 2).
  5. Wählen Sie im nächsten Bildschirm "Erweitert" aus und ändern Sie die Option "Script DROP and CREATE" in "Script DROP and CREATE" (Bild 3).
  6. Wählen Sie, ob Sie das Skript in einem neuen Editorfenster oder in einer Datei speichern und nach Bedarf ausführen möchten.

Auf diese Weise erhalten Sie ein Skript, das alle Ihre Tabellen löscht und neu erstellt, ohne sich um das Debuggen kümmern zu müssen oder ob Sie alles eingeschlossen haben. Dies führt zwar mehr als nur ein Abschneiden aus, die Ergebnisse sind jedoch dieselben. Denken Sie daran, dass Ihre automatisch inkrementierenden Primärschlüssel bei 0 beginnen, im Gegensatz zu abgeschnittenen Tabellen, die sich an den zuletzt zugewiesenen Wert erinnern. Sie können dies auch über Code ausführen, wenn Sie in Ihren PreProd- oder Produktionsumgebungen keinen Zugriff auf Management Studio haben.

1.

Geben Sie hier die Bildbeschreibung ein

2.

Geben Sie hier die Bildbeschreibung ein

3.

Geben Sie hier die Bildbeschreibung ein

Kapitän Kenpachi
quelle
1
Achtung, wenn Sie dies für eine Datenbank mit einem sehr komplexen Schema verwenden. Ich habe es auf einer Entwicklungskopie unserer Produktionsdatenbank versucht und es hat das Schema zerstört, was eine vollständige Neubereitstellung erforderlich machte.
Techrocket9
1
Sie müssen alle Dinge schreiben, die Sie bewahren möchten
Captain Kenpachi
12

Das Abschneiden aller Tabellen funktioniert nur, wenn Sie keine Fremdschlüsselbeziehungen zwischen Ihren Tabellen haben, da Sie mit SQL Server keine Tabelle mit einem Fremdschlüssel abschneiden können.

Eine Alternative dazu besteht darin, die Tabellen mit Fremdschlüsseln zu ermitteln und zuerst aus diesen zu löschen. Anschließend können Sie die Tabellen ohne Fremdschlüssel abschneiden.

Weitere Informationen finden Sie unter http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=65341 und http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72957 .

marcj
quelle
1
Guter Punkt. Daran habe ich nicht gedacht. Möglicherweise kann ich zuerst alle Einschränkungen deaktivieren und sie dann wieder aktivieren, sobald die Daten entfernt wurden.
Ray
7

Eine alternative Option, die ich gerne mit MSSQL Server Deveploper oder Enterprise verwende, besteht darin, unmittelbar nach dem Erstellen des leeren Schemas einen Snapshot der Datenbank zu erstellen. An diesem Punkt können Sie die Datenbank einfach wieder auf den Snapshot zurücksetzen.

Chris Chilvers
quelle
Leider verlieren Sie jedes Mal alle Ihre FULLTEXT-Indizes
Chris KL
6

Tu das nicht! Wirklich keine gute Idee.

Wenn Sie wissen, welche Tabellen Sie abschneiden möchten, erstellen Sie eine gespeicherte Prozedur, die sie abschneidet. Sie können die Bestellung korrigieren, um Fremdschlüsselprobleme zu vermeiden.

Wenn Sie wirklich alle abschneiden möchten (damit Sie sie beispielsweise von BCP laden können), können Sie die Datenbank genauso schnell löschen und eine neue von Grund auf neu erstellen. Dies hat den zusätzlichen Vorteil, dass Sie genau wissen, wo Sie sich befinden.

Ben Liddicott
quelle
Netter alternativer Ansatz hier.
Sam
3
Das Problem bei Ihrem Ansatz besteht darin, dass beim Löschen der Tabellen und der Datenbank alle Berechtigungen verloren gehen, die für verschiedene Anmeldungen und Schemas erteilt wurden. Das neu zu erstellen, wird für große Datenbanken mit vielen Tabellen schmerzhaft sein.
Punit Vora
4

Wenn Sie Daten in einer bestimmten Tabelle (dh einer statischen Nachschlagetabelle) behalten möchten, während Sie Daten in anderen Tabellen innerhalb derselben Datenbank löschen / abschneiden, benötigen Sie eine Schleife mit den darin enthaltenen Ausnahmen. Das habe ich gesucht, als ich auf diese Frage gestoßen bin.

sp_MSForEachTable scheint mir fehlerhaft zu sein (dh inkonsistentes Verhalten mit IF-Anweisungen), weshalb es wahrscheinlich von MS nicht dokumentiert ist.

declare @LastObjectID int = 0
declare @TableName nvarchar(100) = ''
set @LastObjectID = (select top 1 [object_id] from sys.tables where [object_id] > @LastObjectID order by [object_id])
while(@LastObjectID is not null)
begin
    set @TableName = (select top 1 [name] from sys.tables where [object_id] = @LastObjectID)

    if(@TableName not in ('Profiles', 'ClientDetails', 'Addresses', 'AgentDetails', 'ChainCodes', 'VendorDetails'))
    begin
        exec('truncate table [' + @TableName + ']')
    end 

    set @LastObjectID = (select top 1 [object_id] from sys.tables where [object_id] > @LastObjectID order by [object_id])
end
Chris Smith
quelle
4

Der schwierigste Teil beim Abschneiden aller Tabellen besteht darin, die Fremdschlüsseleinschränkungen zu entfernen und erneut hinzuzufügen.

Die folgende Abfrage erstellt die drop & create-Anweisungen für jede Einschränkung, die sich auf jeden Tabellennamen in @myTempTable bezieht. Wenn Sie diese für alle Tabellen generieren möchten, können Sie stattdessen einfach das Informationsschema verwenden, um diese Tabellennamen zu erfassen.

DECLARE @myTempTable TABLE (tableName varchar(200))
INSERT INTO @myTempTable(tableName) VALUES
('TABLE_ONE'),
('TABLE_TWO'),
('TABLE_THREE')


-- DROP FK Contraints
SELECT 'alter table '+quotename(schema_name(ob.schema_id))+
  '.'+quotename(object_name(ob.object_id))+ ' drop constraint ' + quotename(fk.name) 
  FROM sys.objects ob INNER JOIN sys.foreign_keys fk ON fk.parent_object_id = ob.object_id
  WHERE fk.referenced_object_id IN 
      (
         SELECT so.object_id 
         FROM sys.objects so JOIN sys.schemas sc
         ON so.schema_id = sc.schema_id
         WHERE so.name IN (SELECT * FROM @myTempTable)  AND sc.name=N'dbo'  AND type in (N'U'))


 -- CREATE FK Contraints
 SELECT 'ALTER TABLE [PIMSUser].[dbo].[' +cast(c.name as varchar(255)) + '] WITH NOCHECK ADD CONSTRAINT ['+ cast(f.name as varchar(255)) +'] FOREIGN KEY (['+ cast(fc.name as varchar(255)) +'])
      REFERENCES [PIMSUser].[dbo].['+ cast(p.name as varchar(255)) +'] (['+cast(rc.name as varchar(255))+'])'
FROM  sysobjects f
      INNER JOIN sys.sysobjects c ON f.parent_obj = c.id
      INNER JOIN sys.sysreferences r ON f.id = r.constid
      INNER JOIN sys.sysobjects p ON r.rkeyid = p.id
      INNER JOIN sys.syscolumns rc ON r.rkeyid = rc.id and r.rkey1 = rc.colid
      INNER JOIN sys.syscolumns fc ON r.fkeyid = fc.id and r.fkey1 = fc.colid
WHERE 
      f.type = 'F'
      AND
      cast(p.name as varchar(255)) IN (SELECT * FROM @myTempTable)

Ich kopiere dann einfach die auszuführenden Anweisungen aus - aber mit ein wenig Entwicklungsaufwand könnten Sie einen Cursor verwenden, um sie dynamisch auszuführen.

Scott Allen
quelle
3

Es ist viel einfacher (und möglicherweise sogar noch schneller), Ihre Datenbank zu skripten, sie dann einfach zu löschen und aus dem Skript zu erstellen.

AK
quelle
3

Erstellen Sie eine leere "Vorlagendatenbank" und erstellen Sie eine vollständige Sicherung. Wenn Sie eine Aktualisierung benötigen, stellen Sie diese einfach mit WITH REPLACE wieder her. Schnell, einfach, kugelsicher. Und wenn einige Tabellen hier oder da einige Basisdaten benötigen (z. B. Konfigurationsinformationen oder nur grundlegende Informationen, die Ihre App zum Laufen bringen), wird dies auch behandelt.

onupdatecascade
quelle
2

Dies ist einer Möglichkeit, dies zu tun ... es gibt wahrscheinlich 10 andere, die besser / effizienter sind, aber es hört sich so an, als ob dies sehr selten gemacht wird.

Holen Sie sich eine Liste der tablesvon sysobjects, dann durchlaufen Sie diese mit einem Cursor und rufen Sie sp_execsql('truncate table ' + @table_name)für jede auf iteration.

Ben Scheirman
quelle
Beitrag mit SQL hinzugefügt, der genau das tut :) da dies auch das war, wonach ich gesucht habe.
Chris Smith
1

Führen Sie den auskommentierten Abschnitt einmal aus, füllen Sie die Tabelle _TruncateList mit den Tabellen, die abgeschnitten werden sollen, und führen Sie dann den Rest des Skripts aus. Die _ScriptLog-Tabelle muss im Laufe der Zeit bereinigt werden, wenn Sie dies häufig tun.

Sie können dies ändern, wenn Sie alle Tabellen erstellen möchten. Geben Sie einfach den Namen SELECT in #TruncateList FROM sys.tables ein. Normalerweise möchten Sie jedoch nicht alle ausführen.

Dies wirkt sich auch auf alle Fremdschlüssel in der Datenbank aus, und Sie können dies auch ändern, wenn es für Ihre Anwendung zu stumpf ist. Es ist nicht für meine Zwecke.

/*
CREATE TABLE _ScriptLog 
(
    ID Int NOT NULL Identity(1,1)
    , DateAdded DateTime2 NOT NULL DEFAULT GetDate()
    , Script NVarChar(4000) NOT NULL
)

CREATE UNIQUE CLUSTERED INDEX IX_ScriptLog_DateAdded_ID_U_C ON _ScriptLog
(
    DateAdded
    , ID
)

CREATE TABLE _TruncateList
(
    TableName SysName PRIMARY KEY
)
*/
IF OBJECT_ID('TempDB..#DropFK') IS NOT NULL BEGIN
    DROP TABLE #DropFK
END

IF OBJECT_ID('TempDB..#TruncateList') IS NOT NULL BEGIN
    DROP TABLE #TruncateList
END

IF OBJECT_ID('TempDB..#CreateFK') IS NOT NULL BEGIN
    DROP TABLE #CreateFK
END

SELECT Scripts = 'ALTER TABLE ' + '[' + OBJECT_NAME(f.parent_object_id)+ ']'+
' DROP  CONSTRAINT ' + '[' + f.name  + ']'
INTO #DropFK
FROM .sys.foreign_keys AS f
INNER JOIN .sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id

SELECT TableName
INTO #TruncateList
FROM _TruncateList

SELECT Scripts = 'ALTER TABLE ' + const.parent_obj + '
    ADD CONSTRAINT ' + const.const_name + ' FOREIGN KEY (
            ' + const.parent_col_csv + '
            ) REFERENCES ' + const.ref_obj + '(' + const.ref_col_csv + ')
'
INTO #CreateFK
FROM (
    SELECT QUOTENAME(fk.NAME) AS [const_name]
        ,QUOTENAME(schParent.NAME) + '.' + QUOTENAME(OBJECT_name(fkc.parent_object_id)) AS [parent_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcP.parent_object_id, fcp.parent_column_id))
                FROM sys.foreign_key_columns AS fcP
                WHERE fcp.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [parent_col_csv]
        ,QUOTENAME(schRef.NAME) + '.' + QUOTENAME(OBJECT_NAME(fkc.referenced_object_id)) AS [ref_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcR.referenced_object_id, fcR.referenced_column_id))
                FROM sys.foreign_key_columns AS fcR
                WHERE fcR.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [ref_col_csv]
    FROM sys.foreign_key_columns AS fkc
    INNER JOIN sys.foreign_keys AS fk ON fk.object_id = fkc.constraint_object_id
    INNER JOIN sys.objects AS oParent ON oParent.object_id = fkc.parent_object_id
    INNER JOIN sys.schemas AS schParent ON schParent.schema_id = oParent.schema_id
    INNER JOIN sys.objects AS oRef ON oRef.object_id = fkc.referenced_object_id
    INNER JOIN sys.schemas AS schRef ON schRef.schema_id = oRef.schema_id
    GROUP BY fkc.parent_object_id
        ,fkc.referenced_object_id
        ,fk.NAME
        ,fk.object_id
        ,schParent.NAME
        ,schRef.NAME
    ) AS const
ORDER BY const.const_name

INSERT INTO _ScriptLog (Script)
SELECT Scripts
FROM #CreateFK

DECLARE @Cmd NVarChar(4000)
    , @TableName SysName

WHILE 0 < (SELECT Count(1) FROM #DropFK) BEGIN
    SELECT TOP 1 @Cmd = Scripts 
    FROM #DropFK

    EXEC (@Cmd)

    DELETE #DropFK WHERE Scripts = @Cmd
END

WHILE 0 < (SELECT Count(1) FROM #TruncateList) BEGIN
    SELECT TOP 1 @Cmd = N'TRUNCATE TABLE ' +  TableName
        , @TableName = TableName
    FROM #TruncateList

    EXEC (@Cmd)

    DELETE #TruncateList WHERE TableName = @TableName
END

WHILE 0 < (SELECT Count(1) FROM #CreateFK) BEGIN
    SELECT TOP 1 @Cmd = Scripts 
    FROM #CreateFK

    EXEC (@Cmd)

    DELETE #CreateFK WHERE Scripts = @Cmd
END
Steve Hood
quelle
0

Ich verstehe nicht, warum das Löschen von Daten besser wäre als ein Skript zum Löschen und Neuerstellen jeder Tabelle.

Das oder sichern Sie Ihre leere Datenbank und stellen Sie sie über der alten wieder her

Brian Spencer
quelle
2
Der Grund dafür ist, dass der Aufwand für das Löschen und Neuerstellen der Datenbankdateien, Protokolle usw. sehr gering ist. Denken Sie daran, die Datenbank während eines anständigen Unit-Testlaufs 1.000 Mal zu löschen.
Chris KL
0

Bevor Sie die Tabellen abschneiden, müssen Sie alle Fremdschlüssel entfernen. Verwenden Sie dieses Skript , um endgültige Skripte zum Löschen und Neuerstellen aller Fremdschlüssel in der Datenbank zu generieren. Bitte setzen Sie die Variable @action auf 'CREATE' oder 'DROP'.

Edward Weinert
quelle
0

Wählen Sie 'Löschen aus' + TABLE_NAME aus INFORMATION_SCHEMA.TABLES, wobei TABLE_TYPE = 'BASE TABLE'

Woher kommt das Ergebnis?

Kopieren Sie das Abfragefenster, fügen Sie es ein und führen Sie den Befehl aus

Somendra Tiwari
quelle
0

Es ist etwas spät, aber es könnte jemandem helfen. Ich habe manchmal eine Prozedur erstellt, die mit T-SQL Folgendes ausführt:

  1. Speichern Sie alle Einschränkungen in einer temporären Tabelle
  2. Löschen Sie alle Einschränkungen
  3. Schneiden Sie alle Tabellen mit Ausnahme einiger Tabellen ab, für die keine Kürzung erforderlich ist
  4. Erstellen Sie alle Einschränkungen neu.

Ich habe es auf meinem Blog aufgeführt hier

Mohit
quelle