Comment tronquer toutes les tables d'une base de données à l'aide de TSQL?

204

J'ai un environnement de test pour une base de données que je souhaite recharger avec de nouvelles données au début d'un cycle de test. Je ne suis pas intéressé à reconstruire l'intégralité de la base de données, simplement à "redéfinir" les données.

Quelle est la meilleure façon de supprimer toutes les données de toutes les tables à l'aide de TSQL? Existe-t-il des procédures stockées système, des vues, etc. qui peuvent être utilisées? Je ne veux pas créer et maintenir manuellement des instructions de table tronquée pour chaque table - je préférerais qu'elle soit dynamique.

Rayon
la source

Réponses:

188

Pour SQL 2005,

EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'

Quelques liens supplémentaires pour 2000 et 2005/2008 ..

Gulzar Nazim
la source
62
Vous ne pouvez pas tronquer les tables qui ont des clés étrangères, donc cela ne fonctionnera que s'il n'y a pas de contraintes de clé étrangère entre les tables (ou si elles ont été désactivées).
marcj
1
d'accord .. je pensais qu'il avait spécifiquement demandé des tables tronquées, il avait déjà résolu le problème avec les clés étrangères ..
Gulzar Nazim
@ gulzar- en quelque sorte- j'ai posté une question distincte sur la façon de gérer les FK, mais votre réponse se tient sur ses propres mérites.
Ray
11
@Sam: Non, ce ne sera pas le cas! Les données des tableaux ne sont pas pertinentes. Tant qu'il existe une contrainte de clé étrangère référençant la table (même désactivée), vous ne pouvez pas la tronquer.
TToni
3
'EXEC sp_MSForEachTable' DROP TABLE? ' Fonctionne très bien aussi :) (ravissant toutes les tables de la base de données)
kuncevic.dev
419

Lorsque vous traitez la suppression de données de tables qui ont des relations de clé étrangère - ce qui est fondamentalement le cas avec toute base de données correctement conçue - nous pouvons désactiver toutes les contraintes, supprimer toutes les données, puis réactiver les contraintes

-- 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"

Plus d'informations sur la désactivation des contraintes et des déclencheurs ici

si certaines tables ont des colonnes d'identité, nous pouvons vouloir les réensemencer

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

Notez que le comportement de RESEED diffère entre une toute nouvelle table et une table sur laquelle des données avaient été insérées précédemment à partir de BOL :

DBCC CHECKIDENT ('nom_table', RESEED, newReseedValue)

La valeur d'identité actuelle est définie sur newReseedValue. Si aucune ligne n'a été insérée dans la table depuis sa création, la première ligne insérée après l'exécution de DBCC CHECKIDENT utilisera newReseedValue comme identité. Sinon, la ligne suivante insérée utilisera newReseedValue + 1. Si la valeur de newReseedValue est inférieure à la valeur maximale dans la colonne d'identité, le message d'erreur 2627 sera généré lors des références ultérieures à la table.

Merci à Robert d' avoir souligné le fait que la désactivation des contraintes ne permet pas d'utiliser tronquer, les contraintes devraient être supprimées, puis recréées

kristof
la source
34
La désactivation des contraintes NE permettra PAS la troncature des tables référencées par une contrainte FOREIGN KEY. La contrainte FK doit être supprimée. Veuillez répondre si je me trompe, mais je n'ai trouvé aucun moyen d'éviter de les supprimer.
Robert Claypool
1
Un mot clé typo "Table" ne doit pas figurer dans cette instruction EXEC sp_MSForEachTable "DELETE FROM TABLE?" La version correcte doit être: EXEC sp_MSForEachTable "DELETE FROM?"
Raghav
4
Si vous utilisez SSMS 2008 ou plus récent, vous voudrez probablement ajouter SET ROWCOUNT 0au début de votre script car la valeur par défaut est de limiter les actions à 500 lignes! Vous obtiendrez des erreurs frustrantes comme je l'ai fait, car toutes les données n'auront pas été supprimées.
Sean Hanley
1
Cela a très bien fonctionné. Dans mon cas, j'ai également dû ajouter EXEC sp_msforeachtable "ALTER TABLE? Disable TRIGGER all" et EXEC sp_msforeachtable "ALTER TABLE? Enable TRIGGER all" Avant et après l'instruction delete.
RobC
2
Ma réponse préférée. Mais pourquoi (tous, même les commentateurs) placez-vous des chaînes SQL littérales entre guillemets?
bitoolean
57

Voici le roi papa des scripts de nettoyage de base de données. Il effacera toutes les tables et les réamorcera correctement:

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;

Profitez-en, mais faites attention!

Chris KL
la source
2
Malheureusement, la commande ci-dessus échoue si vous avez calculé des colonnes, car sp_MSforeachtable a apparemment SET QUOTED_IDENTITY OFFdans son corps ( lien ). MISE À JOUR: Le correctif consiste à ajouter "SET QUOTED_IDENTIFIERS on;" au début de chaque déclaration qui soulève cette erreur (comme mentionné ici )
Marchy
1
il semble que cela n'ait pas réensemencé mes identités
totooooo
48

La façon la plus simple de procéder consiste à

  1. ouvrir SQL Management Studio
  2. accédez à votre base de données
  3. Faites un clic droit et sélectionnez Tâches-> Générer des scripts (image 1)
  4. Sur l'écran "choisir des objets", sélectionnez l'option "sélectionner des objets spécifiques" et cochez "tableaux" (image 2)
  5. sur l'écran suivant, sélectionnez "avancé" puis changez l'option "Script DROP and CREATE" en "Script DROP and CREATE" (image 3)
  6. Choisissez d'enregistrer le script dans une nouvelle fenêtre de l'éditeur ou un fichier et exécutez-le si nécessaire.

cela vous donnera un script qui supprime et recrée toutes vos tables sans avoir à vous soucier du débogage ou si vous avez tout inclus. Bien que cela effectue plus qu'une simple troncature, les résultats sont les mêmes. Gardez à l'esprit que vos clés primaires à incrémentation automatique commenceront à 0, contrairement aux tables tronquées qui se souviendront de la dernière valeur attribuée. Vous pouvez également l'exécuter à partir du code si vous n'avez pas accès à Management studio sur vos environnements PreProd ou Production.

1.

entrez la description de l'image ici

2.

entrez la description de l'image ici

3.

entrez la description de l'image ici

Capitaine Kenpachi
la source
1
Attention si vous l'utilisez pour une base de données avec un schéma très complexe. Je l'ai essayé sur une copie de développement de notre base de données de production et il a saccagé le schéma, nécessitant un redéploiement total.
Techrocket9
1
Vous devez écrire tout ce que vous voulez conserver
Captain Kenpachi
13

La troncature de toutes les tables ne fonctionnera que si vous n'avez aucune relation de clé étrangère entre vos tables, car SQL Server ne vous permettra pas de tronquer une table avec une clé étrangère.

Une alternative à cela consiste à déterminer les tables avec des clés étrangères et à les supprimer d'abord, vous pouvez ensuite tronquer les tables sans clés étrangères par la suite.

Voir http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=65341 et http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72957 pour plus de détails.

marcj
la source
1
Bon point. Je n'y ai pas pensé. Je pourrais peut-être d'abord désactiver toutes les contraintes, puis les réactiver une fois les données supprimées.
Ray
7

Une autre option que j'aime utiliser avec MSSQL Server Deveploper ou Enterprise est de créer un instantané de la base de données immédiatement après avoir créé le schéma vide. À ce stade, vous pouvez simplement continuer à restaurer la base de données sur l'instantané.

Chris Chilvers
la source
Malheureusement, vous perdez tous vos index FULLTEXT à chaque fois
Chris KL
6

Ne fais pas ça! Vraiment, ce n'est pas une bonne idée.

Si vous savez quelles tables vous souhaitez tronquer, créez une procédure stockée qui les tronque. Vous pouvez corriger la commande pour éviter les problèmes de clé étrangère.

Si vous voulez vraiment tous les tronquer (afin que vous puissiez les charger par BCP par exemple), vous seriez tout aussi rapide de supprimer la base de données et d'en créer une nouvelle à partir de zéro, ce qui aurait l'avantage supplémentaire de savoir exactement où vous êtes.

Ben Liddicott
la source
Belle approche alternative ici.
Sam
3
le problème avec votre approche est que la suppression des tables et de la base de données entraînerait la perte de toutes les autorisations accordées aux différents logins et schémas. Recréer cela va être pénible pour les grandes bases de données avec beaucoup de tables.
Punit Vora
4

Si vous souhaitez conserver des données dans une table particulière (c'est-à-dire une table de recherche statique) tout en supprimant / tronquant des données dans d'autres tables dans la même base de données, vous avez besoin d'une boucle avec les exceptions qu'elle contient. C'est ce que je cherchais lorsque je suis tombé sur cette question.

sp_MSForEachTable me semble bogué (c'est-à-dire un comportement incohérent avec les instructions IF), c'est probablement pourquoi il n'est pas documenté par MS.

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
la source
4

La partie la plus difficile de tronquer toutes les tables consiste à supprimer et à ajouter à nouveau les contraintes de clé étrangère.

La requête suivante crée les instructions drop & create pour chaque contrainte relative à chaque nom de table dans @myTempTable. Si vous souhaitez les générer pour toutes les tables, vous pouvez simplement utiliser le schéma d'informations pour rassembler ces noms de table à la place.

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)

Je copie ensuite simplement les instructions à exécuter - mais avec un peu d'effort de développement, vous pouvez utiliser un curseur pour les exécuter dynamiquement.

Scott Allen
la source
3

Il est beaucoup plus facile (et peut-être même plus rapide) de créer un script pour votre base de données, puis de la déposer et de la créer à partir du script.

AK
la source
3

Créez une base de données "modèle" vide, effectuez une sauvegarde complète. Lorsque vous avez besoin d'actualiser, restaurez simplement à l'aide de WITH REPLACE. Rapide, simple, pare-balles. Et si quelques tables ici ou là ont besoin de certaines données de base (par exemple, des informations de configuration ou simplement des informations de base qui font fonctionner votre application), elles le gèrent également.

onupdatecascade
la source
2

C'est une façon de le faire ... il y en a probablement 10 autres qui sont meilleurs / plus efficaces, mais il semble que cela se fasse très rarement, alors voici ...

obtenir une liste des tablesde sysobjectsboucle puis sur ceux avec un curseur, appelant sp_execsql('truncate table ' + @table_name)pour chaque iteration.

Ben Scheirman
la source
a ajouté un message avec sql qui fait exactement cela :) car c'est ce que je cherchais aussi.
Chris Smith
1

Exécutez la section commentée une fois, remplissez la table _TruncateList avec les tables que vous souhaitez tronquer, puis exécutez le reste du script. La table _ScriptLog devra être nettoyée au fil du temps si vous le faites souvent.

Vous pouvez le modifier si vous voulez faire toutes les tables, il suffit de mettre le nom SELECT INTO #TruncateList FROM sys.tables. Cependant, vous ne voulez généralement pas tout faire.

En outre, cela affectera toutes les clés étrangères de la base de données, et vous pouvez également le modifier s'il est trop contraignant pour votre application. Ce n'est pas pour moi.

/*
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
la source
0

Je ne vois pas pourquoi l'effacement des données serait mieux qu'un script pour supprimer et recréer chaque table.

Cela ou garder une sauvegarde de votre base de données vide et la restaurer sur l'ancienne

Brian Spencer
la source
2
La raison en est que la surcharge de la suppression et de la recréation des fichiers sur la base de données, des journaux, etc. est extrêmement lente. Pensez à essuyer la base de données 1 000 fois au cours d'un test décent.
Chris KL
0

Avant de tronquer les tables, vous devez supprimer toutes les clés étrangères. Utilisez ce script pour générer des scripts finaux pour supprimer et recréer toutes les clés étrangères dans la base de données. Veuillez définir la variable @action sur 'CREATE' ou 'DROP'.

Edward Weinert
la source
0

sélectionnez 'supprimer de' + TABLE_NAME de INFORMATION_SCHEMA.TABLES où TABLE_TYPE = 'BASE TABLE'

d'où vient le résultat.

Copiez et collez sur la fenêtre de requête et exécutez la commande

Somendra Tiwari
la source
0

Il est un peu tard mais cela pourrait aider quelqu'un. J'ai créé une procédure parfois en arrière qui fait ce qui suit en utilisant T-SQL:

  1. Stocker toutes les contraintes dans une table temporaire
  2. Supprimer toutes les contraintes
  3. Tronquer toutes les tables à l'exception de certaines tables, qui n'ont pas besoin de troncature
  4. Recréez toutes les contraintes.

Je l'ai listé sur mon blog ici

Mohit
la source