web-dev-qa-db-fra.com

Impossible de tronquer la table car elle est référencée par une contrainte FOREIGN KEY?

Avec MSSQL2005, puis-je tronquer une table avec une contrainte de clé étrangère si je tronque d’abord la table enfant (la table avec la clé primaire de la relation FK)?

Je sais que je peux soit

  • Utilisez une DELETE sans clause where puis RESEED l'identité (ou)
  • Supprimez le FK, tronquez la table et recréez le FK.

Je pensais que tant que j'aurais tronqué la table enfant avant le parent, je serais bien sans faire l'une des options ci-dessus, mais j'obtiens cette erreur:

Impossible de tronquer la table 'TableName' car elle est référencée par une contrainte FOREIGN KEY.

378
ctrlShiftBryan

Correct; vous ne pouvez pas tronquer une table contenant une contrainte FK.

Mon processus pour cela est généralement le suivant:

  1. Laisser tomber les contraintes
  2. Tronquer la table
  3. Recréez les contraintes.

(Tous dans une transaction, bien sûr.)

Bien entendu, cela ne s'applique que si child a déjà été tronqué. Sinon, j'emprunte un chemin différent, entièrement dépendant de l'aspect de mes données. (Trop de variables pour entrer ici.)

L'affiche originale a déterminé POURQUOI que tel est le cas. voir cette réponse pour plus de détails.

325
John Rudy
DELETE FROM TABLENAME
DBCC CHECKIDENT ('DATABASENAME.dbo.TABLENAME',RESEED, 0)

Notez que ce n'est probablement pas ce que vous voudriez si vous avez des millions d'enregistrements, car c'est très lent.

300
s15199d

Étant donné que TRUNCATE TABLE est une commande DDL , il ne peut pas vérifier si les enregistrements de la table sont référencés par un enregistrement de la table enfant.

C'est pourquoi DELETE fonctionne et que TRUNCATE TABLE ne fonctionne pas: car la base de données est capable de s'assurer qu'elle n'est pas référencée par un autre enregistrement.

167
ctrlShiftBryan

Sans ALTER TABLE

-- Delete all records
DELETE FROM [TableName]
-- Set current ID to "1"
-- If table already contains data, use "0"
-- If table is empty and never insert data, use "1"
-- Use SP https://github.com/reduardo7/TableTruncate
DBCC CHECKIDENT ([TableName], RESEED, [0|1])

Procédure stockée

https://github.com/reduardo7/TableTruncate

Note que ce n'est probablement pas ce que vous voudriez si vous avez des millions d'enregistrements, car c'est très lent.

80
Eduardo Cuomo

La solution fournie par @denver_citizen ci-dessus ne fonctionnait pas pour moi, mais j'aimais bien son esprit et j'ai donc modifié certaines choses:

  • fait une procédure stockée
  • changé la façon dont les clés étrangères sont remplies et recréées
  • le script d'origine tronque toutes les tables référencées, cela peut entraîner une erreur de violation de clé étrangère lorsque la table référencée comporte d'autres références de clé étrangère. Ce script tronque uniquement la table spécifiée en tant que paramètre. Il appartient à l'utilisateur d'appeler plusieurs fois cette procédure stockée sur toutes les tables dans le bon ordre.

Pour le bénéfice du public, voici le script mis à jour:

CREATE PROCEDURE [dbo].[truncate_non_empty_table]

  @TableToTruncate                 VARCHAR(64)

AS 

BEGIN

SET NOCOUNT ON

-- GLOBAL VARIABLES
DECLARE @i int
DECLARE @Debug bit
DECLARE @Recycle bit
DECLARE @Verbose bit
DECLARE @TableName varchar(80)
DECLARE @ColumnName varchar(80)
DECLARE @ReferencedTableName varchar(80)
DECLARE @ReferencedColumnName varchar(80)
DECLARE @ConstraintName varchar(250)

DECLARE @CreateStatement varchar(max)
DECLARE @DropStatement varchar(max)   
DECLARE @TruncateStatement varchar(max)
DECLARE @CreateStatementTemp varchar(max)
DECLARE @DropStatementTemp varchar(max)
DECLARE @TruncateStatementTemp varchar(max)
DECLARE @Statement varchar(max)

        -- 1 = Will not execute statements 
 SET @Debug = 0
        -- 0 = Will not create or truncate storage table
        -- 1 = Will create or truncate storage table
 SET @Recycle = 0
        -- 1 = Will print a message on every step
 set @Verbose = 1

 SET @i = 1
    SET @CreateStatement = 'ALTER TABLE [dbo].[<tablename>]  WITH NOCHECK ADD  CONSTRAINT [<constraintname>] FOREIGN KEY([<column>]) REFERENCES [dbo].[<reftable>] ([<refcolumn>])'
    SET @DropStatement = 'ALTER TABLE [dbo].[<tablename>] DROP CONSTRAINT [<constraintname>]'
    SET @TruncateStatement = 'TRUNCATE TABLE [<tablename>]'

-- Drop Temporary tables

IF OBJECT_ID('tempdb..#FKs') IS NOT NULL
    DROP TABLE #FKs

-- GET FKs
SELECT ROW_NUMBER() OVER (ORDER BY OBJECT_NAME(parent_object_id), clm1.name) as ID,
       OBJECT_NAME(constraint_object_id) as ConstraintName,
       OBJECT_NAME(parent_object_id) as TableName,
       clm1.name as ColumnName, 
       OBJECT_NAME(referenced_object_id) as ReferencedTableName,
       clm2.name as ReferencedColumnName
  INTO #FKs
  FROM sys.foreign_key_columns fk
       JOIN sys.columns clm1 
         ON fk.parent_column_id = clm1.column_id 
            AND fk.parent_object_id = clm1.object_id
       JOIN sys.columns clm2
         ON fk.referenced_column_id = clm2.column_id 
            AND fk.referenced_object_id= clm2.object_id
 --WHERE OBJECT_NAME(parent_object_id) not in ('//tables that you do not wont to be truncated')
 WHERE OBJECT_NAME(referenced_object_id) = @TableToTruncate
 ORDER BY OBJECT_NAME(parent_object_id)


-- Prepare Storage Table
IF Not EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Internal_FK_Definition_Storage')
   BEGIN
        IF @Verbose = 1
     PRINT '1. Creating Process Specific Tables...'

  -- CREATE STORAGE TABLE IF IT DOES NOT EXISTS
  CREATE TABLE [Internal_FK_Definition_Storage] 
  (
   ID int not null identity(1,1) primary key,
   FK_Name varchar(250) not null,
   FK_CreationStatement varchar(max) not null,
   FK_DestructionStatement varchar(max) not null,
   Table_TruncationStatement varchar(max) not null
  ) 
   END 
ELSE
   BEGIN
        IF @Recycle = 0
            BEGIN
                IF @Verbose = 1
       PRINT '1. Truncating Process Specific Tables...'

    -- TRUNCATE TABLE IF IT ALREADY EXISTS
    TRUNCATE TABLE [Internal_FK_Definition_Storage]    
      END
      ELSE
         PRINT '1. Process specific table will be recycled from previous execution...'
   END


IF @Recycle = 0
   BEGIN

  IF @Verbose = 1
     PRINT '2. Backing up Foreign Key Definitions...'

  -- Fetch and persist FKs             
  WHILE (@i <= (SELECT MAX(ID) FROM #FKs))
   BEGIN
    SET @ConstraintName = (SELECT ConstraintName FROM #FKs WHERE ID = @i)
    SET @TableName = (SELECT TableName FROM #FKs WHERE ID = @i)
    SET @ColumnName = (SELECT ColumnName FROM #FKs WHERE ID = @i)
    SET @ReferencedTableName = (SELECT ReferencedTableName FROM #FKs WHERE ID = @i)
    SET @ReferencedColumnName = (SELECT ReferencedColumnName FROM #FKs WHERE ID = @i)

    SET @DropStatementTemp = REPLACE(REPLACE(@DropStatement,'<tablename>',@TableName),'<constraintname>',@ConstraintName)
    SET @CreateStatementTemp = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@CreateStatement,'<tablename>',@TableName),'<column>',@ColumnName),'<constraintname>',@ConstraintName),'<reftable>',@ReferencedTableName),'<refcolumn>',@ReferencedColumnName)
    SET @TruncateStatementTemp = REPLACE(@TruncateStatement,'<tablename>',@TableName) 

    INSERT INTO [Internal_FK_Definition_Storage]
                        SELECT @ConstraintName, @CreateStatementTemp, @DropStatementTemp, @TruncateStatementTemp

    SET @i = @i + 1

    IF @Verbose = 1
       PRINT '  > Backing up [' + @ConstraintName + '] from [' + @TableName + ']'

    END   
    END   
    ELSE 
       PRINT '2. Backup up was recycled from previous execution...'

       IF @Verbose = 1
     PRINT '3. Dropping Foreign Keys...'

    -- DROP FOREING KEYS
    SET @i = 1
    WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
          BEGIN
             SET @ConstraintName = (SELECT FK_Name FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    SET @Statement = (SELECT FK_DestructionStatement FROM [Internal_FK_Definition_Storage] WITH (NOLOCK) WHERE ID = @i)

    IF @Debug = 1 
       PRINT @Statement
    ELSE
       EXEC(@Statement)

    SET @i = @i + 1


    IF @Verbose = 1
       PRINT '  > Dropping [' + @ConstraintName + ']'

             END     


    IF @Verbose = 1
       PRINT '4. Truncating Tables...'

    -- TRUNCATE TABLES
-- SzP: commented out as the tables to be truncated might also contain tables that has foreign keys
-- to resolve this the stored procedure should be called recursively, but I dont have the time to do it...          
 /*
    SET @i = 1
    WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
          BEGIN

    SET @Statement = (SELECT Table_TruncationStatement FROM [Internal_FK_Definition_Storage] WHERE ID = @i)

    IF @Debug = 1 
       PRINT @Statement
    ELSE
       EXEC(@Statement)

    SET @i = @i + 1

    IF @Verbose = 1
       PRINT '  > ' + @Statement
          END
*/          


    IF @Verbose = 1
       PRINT '  > TRUNCATE TABLE [' + @TableToTruncate + ']'

    IF @Debug = 1 
        PRINT 'TRUNCATE TABLE [' + @TableToTruncate + ']'
    ELSE
        EXEC('TRUNCATE TABLE [' + @TableToTruncate + ']')


    IF @Verbose = 1
       PRINT '5. Re-creating Foreign Keys...'

    -- CREATE FOREING KEYS
    SET @i = 1
    WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
          BEGIN
             SET @ConstraintName = (SELECT FK_Name FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    SET @Statement = (SELECT FK_CreationStatement FROM [Internal_FK_Definition_Storage] WHERE ID = @i)

    IF @Debug = 1 
       PRINT @Statement
    ELSE
       EXEC(@Statement)

    SET @i = @i + 1


    IF @Verbose = 1
       PRINT '  > Re-creating [' + @ConstraintName + ']'

          END

    IF @Verbose = 1
       PRINT '6. Process Completed'


END
54
Peter Szanto

utilisez la commande suivante après la suppression de toutes les lignes de cette table à l'aide de l'instruction delete

delete from tablename

DBCC CHECKIDENT ('tablename', RESEED, 0)

EDIT: syntaxe corrigée pour SQL Server

18
abdelwahed

vous pouvez suivre cette étape, En reseeding table, vous pouvez supprimer les données de la table.

delete from table_name
dbcc checkident('table_name',reseed,0)

si une erreur survient, vous devez réensemencer la table primaire.

11
Rajneesh Kumar

Voici un script que j'ai écrit afin d'automatiser le processus. J'espère que ça aide.

SET NOCOUNT ON

-- GLOBAL VARIABLES
DECLARE @i int
DECLARE @Debug bit
DECLARE @Recycle bit
DECLARE @Verbose bit
DECLARE @TableName varchar(80)
DECLARE @ColumnName varchar(80)
DECLARE @ReferencedTableName varchar(80)
DECLARE @ReferencedColumnName varchar(80)
DECLARE @ConstraintName varchar(250)

DECLARE @CreateStatement varchar(max)
DECLARE @DropStatement varchar(max)   
DECLARE @TruncateStatement varchar(max)
DECLARE @CreateStatementTemp varchar(max)
DECLARE @DropStatementTemp varchar(max)
DECLARE @TruncateStatementTemp varchar(max)
DECLARE @Statement varchar(max)

        -- 1 = Will not execute statements 
 SET @Debug = 0
        -- 0 = Will not create or truncate storage table
        -- 1 = Will create or truncate storage table
 SET @Recycle = 0
        -- 1 = Will print a message on every step
 set @Verbose = 1

 SET @i = 1
    SET @CreateStatement = 'ALTER TABLE [dbo].[<tablename>]  WITH NOCHECK ADD  CONSTRAINT [<constraintname>] FOREIGN KEY([<column>]) REFERENCES [dbo].[<reftable>] ([<refcolumn>])'
    SET @DropStatement = 'ALTER TABLE [dbo].[<tablename>] DROP CONSTRAINT [<constraintname>]'
    SET @TruncateStatement = 'TRUNCATE TABLE [<tablename>]'

-- Drop Temporary tables
DROP TABLE #FKs

-- GET FKs
SELECT ROW_NUMBER() OVER (ORDER BY OBJECT_NAME(parent_object_id), clm1.name) as ID,
       OBJECT_NAME(constraint_object_id) as ConstraintName,
       OBJECT_NAME(parent_object_id) as TableName,
       clm1.name as ColumnName, 
       OBJECT_NAME(referenced_object_id) as ReferencedTableName,
       clm2.name as ReferencedColumnName
  INTO #FKs
  FROM sys.foreign_key_columns fk
       JOIN sys.columns clm1 
         ON fk.parent_column_id = clm1.column_id 
            AND fk.parent_object_id = clm1.object_id
       JOIN sys.columns clm2
         ON fk.referenced_column_id = clm2.column_id 
            AND fk.referenced_object_id= clm2.object_id
 WHERE OBJECT_NAME(parent_object_id) not in ('//tables that you do not wont to be truncated')
 ORDER BY OBJECT_NAME(parent_object_id)


-- Prepare Storage Table
IF Not EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Internal_FK_Definition_Storage')
   BEGIN
        IF @Verbose = 1
     PRINT '1. Creating Process Specific Tables...'

  -- CREATE STORAGE TABLE IF IT DOES NOT EXISTS
  CREATE TABLE [Internal_FK_Definition_Storage] 
  (
   ID int not null identity(1,1) primary key,
   FK_Name varchar(250) not null,
   FK_CreationStatement varchar(max) not null,
   FK_DestructionStatement varchar(max) not null,
   Table_TruncationStatement varchar(max) not null
  ) 
   END 
ELSE
   BEGIN
        IF @Recycle = 0
            BEGIN
                IF @Verbose = 1
       PRINT '1. Truncating Process Specific Tables...'

    -- TRUNCATE TABLE IF IT ALREADY EXISTS
    TRUNCATE TABLE [Internal_FK_Definition_Storage]    
      END
      ELSE
         PRINT '1. Process specific table will be recycled from previous execution...'
   END

IF @Recycle = 0
   BEGIN

  IF @Verbose = 1
     PRINT '2. Backing up Foreign Key Definitions...'

  -- Fetch and persist FKs             
  WHILE (@i <= (SELECT MAX(ID) FROM #FKs))
   BEGIN
    SET @ConstraintName = (SELECT ConstraintName FROM #FKs WHERE ID = @i)
    SET @TableName = (SELECT TableName FROM #FKs WHERE ID = @i)
    SET @ColumnName = (SELECT ColumnName FROM #FKs WHERE ID = @i)
    SET @ReferencedTableName = (SELECT ReferencedTableName FROM #FKs WHERE ID = @i)
    SET @ReferencedColumnName = (SELECT ReferencedColumnName FROM #FKs WHERE ID = @i)

    SET @DropStatementTemp = REPLACE(REPLACE(@DropStatement,'<tablename>',@TableName),'<constraintname>',@ConstraintName)
    SET @CreateStatementTemp = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@CreateStatement,'<tablename>',@TableName),'<column>',@ColumnName),'<constraintname>',@ConstraintName),'<reftable>',@ReferencedTableName),'<refcolumn>',@ReferencedColumnName)
    SET @TruncateStatementTemp = REPLACE(@TruncateStatement,'<tablename>',@TableName) 

    INSERT INTO [Internal_FK_Definition_Storage]
                        SELECT @ConstraintName, @CreateStatementTemp, @DropStatementTemp, @TruncateStatementTemp

    SET @i = @i + 1

    IF @Verbose = 1
       PRINT '  > Backing up [' + @ConstraintName + '] from [' + @TableName + ']'

   END
    END   
    ELSE 
       PRINT '2. Backup up was recycled from previous execution...'

       IF @Verbose = 1
     PRINT '3. Dropping Foreign Keys...'

    -- DROP FOREING KEYS
    SET @i = 1
    WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
          BEGIN
             SET @ConstraintName = (SELECT FK_Name FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    SET @Statement = (SELECT FK_DestructionStatement FROM [Internal_FK_Definition_Storage] WITH (NOLOCK) WHERE ID = @i)

    IF @Debug = 1 
       PRINT @Statement
    ELSE
       EXEC(@Statement)

    SET @i = @i + 1

    IF @Verbose = 1
       PRINT '  > Dropping [' + @ConstraintName + ']'
             END     

    IF @Verbose = 1
       PRINT '4. Truncating Tables...'

    -- TRUNCATE TABLES
    SET @i = 1
    WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
          BEGIN
    SET @Statement = (SELECT Table_TruncationStatement FROM [Internal_FK_Definition_Storage] WHERE ID = @i)

    IF @Debug = 1 
       PRINT @Statement
    ELSE
       EXEC(@Statement)

    SET @i = @i + 1

    IF @Verbose = 1
       PRINT '  > ' + @Statement
          END

    IF @Verbose = 1
       PRINT '5. Re-creating Foreign Keys...'

    -- CREATE FOREING KEYS
    SET @i = 1
    WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
          BEGIN
             SET @ConstraintName = (SELECT FK_Name FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    SET @Statement = (SELECT FK_CreationStatement FROM [Internal_FK_Definition_Storage] WHERE ID = @i)

    IF @Debug = 1 
       PRINT @Statement
    ELSE
       EXEC(@Statement)

    SET @i = @i + 1

    IF @Verbose = 1
       PRINT '  > Re-creating [' + @ConstraintName + ']'
          END

    IF @Verbose = 1
       PRINT '6. Process Completed'
11
denver_citizen

Trouvé ailleurs sur le web

EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'
EXEC sp_MSForEachTable 'ALTER TABLE ? DISABLE TRIGGER ALL'
-- EXEC sp_MSForEachTable 'DELETE FROM ?' -- Uncomment to execute
EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'
EXEC sp_MSForEachTable 'ALTER TABLE ? ENABLE TRIGGER ALL'
9
Freddie Bell

Eh bien, je n'ai pas trouvé exemples de la solution {très simple} que j'ai utilisée, à savoir

  1. Déposer la clé étrangère;
  2. Table tronquée
  3. Recréer une clé étrangère

Ici ça va:

1) Recherchez le nom de la clé étrangère à l'origine de l'échec (par exemple: FK_PROBLEM_REASON, avec le champ ID, dans la table TABLE_OWNING_CONSTRAINT) 2) Supprimez cette clé de la table:

ALTER TABLE TABLE_OWNING_CONSTRAINT DROP CONSTRAINT FK_PROBLEM_REASON

3) tronquer la table voulue

TRUNCATE TABLE TABLE_TO_TRUNCATE

4) Ajoutez de nouveau la clé à cette première table:

ALTER TABLE TABLE_OWNING_CONSTRAINT ADD CONSTRAINT FK_PROBLEM_REASON FOREIGN KEY(ID) REFERENCES TABLE_TO_TRUNCATE (ID)

C'est tout.

Si je vous ai bien compris, vous voulez avez un environnement propre à configurer pour les bases de données impliquant des tests d’intégration.

Mon approche ici serait de supprimer tout le schéma et de le recréer plus tard. 

Les raisons:

  1. Vous avez probablement déjà un script "créer un schéma". Il est facile de le réutiliser pour l'isolation de test.
  2. La création d'un schéma est assez rapide.
  3. Avec cette approche, il est assez facile de configurer votre script pour que chaque appareil crée un nouveau schéma (avec un nom temporaire), puis vous pouvez commencer à exécuter des appareils de test en parallèle, ce qui accélère considérablement la partie la plus lente de votre suite de tests .
6
Ken Egozi

Vous ne pouvez pas tronquer une table si vous ne supprimez pas les contraintes. Une désactivation ne fonctionne pas non plus. vous devez tout laisser tomber. J'ai fait un script qui supprime toutes les contraintes, puis recrée ensuite.

Assurez-vous de l'envelopper dans une transaction;)

SET NOCOUNT ON
GO

DECLARE @table TABLE(
RowId INT PRIMARY KEY IDENTITY(1, 1),
ForeignKeyConstraintName NVARCHAR(200),
ForeignKeyConstraintTableSchema NVARCHAR(200),
ForeignKeyConstraintTableName NVARCHAR(200),
ForeignKeyConstraintColumnName NVARCHAR(200),
PrimaryKeyConstraintName NVARCHAR(200),
PrimaryKeyConstraintTableSchema NVARCHAR(200),
PrimaryKeyConstraintTableName NVARCHAR(200),
PrimaryKeyConstraintColumnName NVARCHAR(200)
)

INSERT INTO @table(ForeignKeyConstraintName, ForeignKeyConstraintTableSchema, ForeignKeyConstraintTableName, ForeignKeyConstraintColumnName)
SELECT
U.CONSTRAINT_NAME,
U.TABLE_SCHEMA,
U.TABLE_NAME,
U.COLUMN_NAME
FROM
INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
ON U.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
C.CONSTRAINT_TYPE = 'FOREIGN KEY'

UPDATE @table SET
PrimaryKeyConstraintName = UNIQUE_CONSTRAINT_NAME
FROM
@table T
INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
ON T.ForeignKeyConstraintName = R.CONSTRAINT_NAME

UPDATE @table SET
PrimaryKeyConstraintTableSchema = TABLE_SCHEMA,
PrimaryKeyConstraintTableName = TABLE_NAME
FROM @table T
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
ON T.PrimaryKeyConstraintName = C.CONSTRAINT_NAME

UPDATE @table SET
PrimaryKeyConstraintColumnName = COLUMN_NAME
FROM @table T
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
ON T.PrimaryKeyConstraintName = U.CONSTRAINT_NAME

--DROP CONSTRAINT:

DECLARE @dynSQL varchar(MAX);

DECLARE cur CURSOR FOR
SELECT
'
ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + ']
DROP CONSTRAINT ' + ForeignKeyConstraintName + '
'
FROM
@table

OPEN cur

FETCH cur into @dynSQL
WHILE @@FETCH_STATUS = 0 
BEGIN
    exec(@dynSQL)
    print @dynSQL

    FETCH cur into @dynSQL
END
CLOSE cur
DEALLOCATE cur
---------------------



   --HERE GOES YOUR TRUNCATES!!!!!
   --HERE GOES YOUR TRUNCATES!!!!!
   --HERE GOES YOUR TRUNCATES!!!!!

    truncate table your_table

   --HERE GOES YOUR TRUNCATES!!!!!
   --HERE GOES YOUR TRUNCATES!!!!!
   --HERE GOES YOUR TRUNCATES!!!!!

---------------------
--ADD CONSTRAINT:

DECLARE cur2 CURSOR FOR
SELECT
'
ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + ']
ADD CONSTRAINT ' + ForeignKeyConstraintName + ' FOREIGN KEY(' + ForeignKeyConstraintColumnName + ') REFERENCES [' + PrimaryKeyConstraintTableSchema + '].[' + PrimaryKeyConstraintTableName + '](' + PrimaryKeyConstraintColumnName + ')
'
FROM
@table

OPEN cur2

FETCH cur2 into @dynSQL
WHILE @@FETCH_STATUS = 0 
BEGIN
    exec(@dynSQL)

    print @dynSQL

    FETCH cur2 into @dynSQL
END
CLOSE cur2
DEALLOCATE cur2
5
renanleandrof
SET FOREIGN_KEY_CHECKS = 0; 

truncate table "yourTableName";

SET FOREIGN_KEY_CHECKS = 1;
4
Victor Jimenez

tronquer n'a pas fonctionné pour moi, supprimer + rediffuser est la meilleure solution . Si certains d'entre vous ont besoin d'itérer sur un très grand nombre de tables pour exécuter delete + reseed, vous pouvez rencontrer des problèmes avec certains tables qui ne possèdent pas de colonne d'identité, le code suivant vérifie si la colonne d'identité existe avant de tenter de réensemencer

    EXEC ('DELETE FROM [schemaName].[tableName]')
    IF EXISTS (Select * from sys.identity_columns where object_name(object_id) = 'tableName')
    BEGIN
        EXEC ('DBCC CHECKIDENT ([schemaName.tableName], RESEED, 0)')
    END
3
Ji_in_coding

C'est ma solution à ce problème. Je l'ai utilisé pour modifier PK, mais idée la même chose. J'espère que cela vous sera utile)

PRINT 'Script starts'

DECLARE @foreign_key_name varchar(255)
DECLARE @keycnt int
DECLARE @foreign_table varchar(255)
DECLARE @foreign_column_1 varchar(255)
DECLARE @foreign_column_2 varchar(255)
DECLARE @primary_table varchar(255)
DECLARE @primary_column_1 varchar(255)
DECLARE @primary_column_2 varchar(255)
DECLARE @TablN varchar(255)

-->> Type the primary table name
SET @TablN = ''
---------------------------------------------------------------------------------------    ------------------------------
--Here will be created the temporary table with all reference FKs
---------------------------------------------------------------------------------------------------------------------
PRINT 'Creating the temporary table'
select cast(f.name  as varchar(255)) as foreign_key_name
    , r.keycnt
    , cast(c.name as  varchar(255)) as foreign_table
    , cast(fc.name as varchar(255)) as  foreign_column_1
    , cast(fc2.name as varchar(255)) as foreign_column_2
    , cast(p.name as varchar(255)) as primary_table
    , cast(rc.name as varchar(255))  as primary_column_1
    , cast(rc2.name as varchar(255)) as  primary_column_2
    into #ConTab
    from sysobjects f
    inner join sysobjects c on  f.parent_obj = c.id 
    inner join sysreferences r on f.id =  r.constid
    inner join sysobjects p on r.rkeyid = p.id
    inner  join syscolumns rc on r.rkeyid = rc.id and r.rkey1 = rc.colid
    inner  join syscolumns fc on r.fkeyid = fc.id and r.fkey1 = fc.colid
    left join  syscolumns rc2 on r.rkeyid = rc2.id and r.rkey2 = rc.colid
    left join  syscolumns fc2 on r.fkeyid = fc2.id and r.fkey2 = fc.colid
    where f.type =  'F' and p.name = @TablN
 ORDER BY cast(p.name as varchar(255))
---------------------------------------------------------------------------------------------------------------------
--Cursor, below, will drop all reference FKs
---------------------------------------------------------------------------------------------------------------------
DECLARE @CURSOR CURSOR
/*Fill in cursor*/

PRINT 'Cursor 1 starting. All refernce FK will be droped'

SET @CURSOR  = CURSOR SCROLL
FOR
select foreign_key_name
    , keycnt
    , foreign_table
    , foreign_column_1
    , foreign_column_2
    , primary_table
    , primary_column_1
    , primary_column_2
    from #ConTab

OPEN @CURSOR

FETCH NEXT FROM @CURSOR INTO @foreign_key_name, @keycnt, @foreign_table,         @foreign_column_1, @foreign_column_2, 
                        @primary_table, @primary_column_1, @primary_column_2

WHILE @@FETCH_STATUS = 0
BEGIN

    EXEC ('ALTER TABLE ['+@foreign_table+'] DROP CONSTRAINT ['+@foreign_key_name+']')

FETCH NEXT FROM @CURSOR INTO @foreign_key_name, @keycnt, @foreign_table, @foreign_column_1, @foreign_column_2, 
                         @primary_table, @primary_column_1, @primary_column_2
END
CLOSE @CURSOR
PRINT 'Cursor 1 finished work'
---------------------------------------------------------------------------------------------------------------------
--Here you should provide the chainging script for the primary table
---------------------------------------------------------------------------------------------------------------------

PRINT 'Altering primary table begin'

TRUNCATE TABLE table_name

PRINT 'Altering finished'

---------------------------------------------------------------------------------------------------------------------
--Cursor, below, will add again all reference FKs
--------------------------------------------------------------------------------------------------------------------

PRINT 'Cursor 2 starting. All refernce FK will added'
SET @CURSOR  = CURSOR SCROLL
FOR
select foreign_key_name
    , keycnt
    , foreign_table
    , foreign_column_1
    , foreign_column_2
    , primary_table
    , primary_column_1
    , primary_column_2
    from #ConTab

OPEN @CURSOR

FETCH NEXT FROM @CURSOR INTO @foreign_key_name, @keycnt, @foreign_table, @foreign_column_1, @foreign_column_2, 
                         @primary_table, @primary_column_1, @primary_column_2

WHILE @@FETCH_STATUS = 0
BEGIN

    EXEC ('ALTER TABLE [' +@foreign_table+ '] WITH NOCHECK ADD  CONSTRAINT [' +@foreign_key_name+ '] FOREIGN KEY(['+@foreign_column_1+'])
        REFERENCES [' +@primary_table+'] (['+@primary_column_1+'])')

    EXEC ('ALTER TABLE [' +@foreign_table+ '] CHECK CONSTRAINT [' +@foreign_key_name+']')

FETCH NEXT FROM @CURSOR INTO @foreign_key_name, @keycnt, @foreign_table, @foreign_column_1, @foreign_column_2, 
                         @primary_table, @primary_column_1, @primary_column_2
END
CLOSE @CURSOR
PRINT 'Cursor 2 finished work'
---------------------------------------------------------------------------------------------------------------------
PRINT 'Temporary table droping'
drop table #ConTab
PRINT 'Finish'
2
Oleg

J'écris les manières suivantes et j'ai essayé de les paramétrer, donc vous pouvez Les exécuter dans un Query document ou Faire une SP utile avec eux.

A) Supprimer

Si votre table n'a pas des millions d'enregistrements cela fonctionne bien et n'a pas de commandes Alter:

---------------------------------------------------------------
------------------- Just Fill Parameters Value ----------------
---------------------------------------------------------------
DECLARE @DbName AS NVARCHAR(30) = 'MyDb'         --< Db Name
DECLARE @Schema AS NVARCHAR(30) = 'dbo'          --< Schema
DECLARE @TableName AS NVARCHAR(30) = 'Book'      --< Table Name
------------------ /Just Fill Parameters Value ----------------

DECLARE @Query AS NVARCHAR(500) = 'Delete FROM ' + @TableName

EXECUTE sp_executesql @Query
SET @Query=@DbName+'.'+@Schema+'.'+@TableName
DBCC CHECKIDENT (@Query,RESEED, 0)
  • Dans la réponse ci-dessus, la méthode de résolution du problème mentionné dans la question est basée sur @ s15199danswer .

B) tronquer

Si votre table contient des millions d'enregistrements ou si vous n'avez aucun problème avec modifier la commande dans vos codes, utilisez celui-ci:

--   Book                               Student
--
--   |  BookId  | Field1 |              | StudentId |  BookId  |
--   ---------------------              ------------------------ 
--   |    1     |    A   |              |     2     |    1     |  
--   |    2     |    B   |              |     1     |    1     |
--   |    3     |    C   |              |     2     |    3     |  

---------------------------------------------------------------
------------------- Just Fill Parameters Value ----------------
---------------------------------------------------------------
DECLARE @DbName AS NVARCHAR(30) = 'MyDb'
DECLARE @Schema AS NVARCHAR(30) = 'dbo'
DECLARE @TableName_ToTruncate AS NVARCHAR(30) = 'Book'

DECLARE @TableName_OfOwnerOfConstraint AS NVARCHAR(30) = 'Student' --< Decelations About FK_Book_Constraint
DECLARE @Ref_ColumnName_In_TableName_ToTruncate AS NVARCHAR(30) = 'BookId' --< Decelations About FK_Book_Constraint
DECLARE @FK_ColumnName_In_TableOfOwnerOfConstraint AS NVARCHAR(30) = 'Fk_BookId' --< Decelations About FK_Book_Constraint
DECLARE @FK_ConstraintName AS NVARCHAR(30) = 'FK_Book_Constraint'                --< Decelations About FK_Book_Constraint
------------------ /Just Fill Parameters Value ----------------

DECLARE @Query AS NVARCHAR(2000)

SET @Query= 'ALTER TABLE '+@TableName_OfOwnerOfConstraint+' DROP CONSTRAINT '+@FK_ConstraintName
EXECUTE sp_executesql @Query

SET @Query= 'Truncate Table '+ @TableName_ToTruncate
EXECUTE sp_executesql @Query

SET @Query= 'ALTER TABLE '+@TableName_OfOwnerOfConstraint+' ADD CONSTRAINT '+@FK_ConstraintName+' FOREIGN KEY('+@FK_ColumnName_In_TableOfOwnerOfConstraint+') REFERENCES '+@TableName_ToTruncate+'('+@Ref_ColumnName_In_TableName_ToTruncate+')'
EXECUTE sp_executesql @Query
  • Dans la réponse ci-dessus, la méthode de résolution du problème mentionné dans la question est basée sur @LauroWolffValenteSobrinho} _ answer .

  • Si vous avez plus d’une contrainte, vous devez ajouter ses codes comme moi à la requête ci-dessus.

  • Vous pouvez également modifier la base de code ci-dessus @SerjSagananswer pour désactiver et activer la contrainte.

2
RAM

Ce qui suit fonctionne pour moi même avec des contraintes FK, et combine les réponses suivantes à ne supprime que les tables spécifiées:


USE [YourDB];

DECLARE @TransactionName varchar(20) = 'stopdropandroll';

BEGIN TRAN @TransactionName;
set xact_abort on; /* automatic rollback https://stackoverflow.com/a/1749788/1037948 */
    -- ===== DO WORK // =====

    -- dynamic sql placeholder
    DECLARE @SQL varchar(300);

    -- LOOP: https://stackoverflow.com/a/10031803/1037948
    -- list of things to loop
    DECLARE @delim char = ';';
    DECLARE @foreach varchar(MAX) = 'Table;Names;Separated;By;Delimiter' + @delim + 'AnotherName' + @delim + 'Still Another';
    DECLARE @token varchar(MAX);
    WHILE len(@foreach) > 0
    BEGIN
        -- set current loop token
        SET @token = left(@foreach, charindex(@delim, @foreach+@delim)-1)
        -- ======= DO WORK // ===========

        -- dynamic sql (parentheses are required): https://stackoverflow.com/a/989111/1037948
        SET @SQL = 'DELETE FROM [' + @token + ']; DBCC CHECKIDENT (''' + @token + ''',RESEED, 0);'; -- https://stackoverflow.com/a/11784890
        PRINT @SQL;
        EXEC (@SQL);

        -- ======= // END WORK ===========
        -- continue loop, chopping off token
        SET @foreach = stuff(@foreach, 1, charindex(@delim, @foreach+@delim), '')
    END

    -- ===== // END WORK =====
-- review and commit
SELECT @@TRANCOUNT as TransactionsPerformed, @@ROWCOUNT as LastRowsChanged;
COMMIT TRAN @TransactionName;

Remarque:

Je pense qu’il est toujours utile de déclarer les tables dans l’ordre dans lequel vous souhaitez les supprimer (c’est-à-dire tuer les dépendances en premier). Comme indiqué dans cette réponse , plutôt que des noms de boucle spécifiques, vous pouvez remplacer toutes les tables par

EXEC sp_MSForEachTable 'DELETE FROM ?; DBCC CHECKIDENT (''?'',RESEED, 0);';
2
drzaus

Pour MS SQL, au moins les versions les plus récentes, vous pouvez simplement désactiver les contraintes avec un code comme celui-ci:

ALTER TABLE Orders
NOCHECK CONSTRAINT [FK_dbo.Orders_dbo.Customers_Customer_Id]
GO

TRUNCATE TABLE Customers
GO

ALTER TABLE Orders
WITH CHECK CHECK CONSTRAINT [FK_dbo.Orders_dbo.Customers_Customer_Id]
GO
2
Serj Sagan

La réponse de @ denver_citizen n'a pas fonctionné pour moi, mais je l'ai modifiée pour tenir compte de:

  1. Touches composites
  2. Actions de suppression et de mise à jour
  3. Vérification de l'index lors de l'ajout de nouveau 
  4. Schémas autres que dbo
  5. Plusieurs tables à la fois
DECLARE @Debug bit = 0;

-- List of tables to truncate
select
    SchemaName, Name
into #tables
from (values 
    ('schema', 'table')
    ,('schema2', 'table2')
) as X(SchemaName, Name)


BEGIN TRANSACTION TruncateTrans;

with foreignKeys AS (
    SELECT 
        SCHEMA_NAME(fk.schema_id) as SchemaName
        ,fk.Name as ConstraintName
        ,OBJECT_NAME(fk.parent_object_id) as TableName
        ,OBJECT_NAME(fk.referenced_object_id) as ReferencedTableName
        ,fc.constraint_column_id
        ,COL_NAME(fk.parent_object_id, fc.parent_column_id) AS ColumnName
        ,COL_NAME(fk.referenced_object_id, fc.referenced_column_id) as ReferencedColumnName
        ,fk.delete_referential_action_desc
        ,fk.update_referential_action_desc
    FROM sys.foreign_keys AS fk
        JOIN sys.foreign_key_columns AS fc
            ON fk.object_id = fc.constraint_object_id
        JOIN #tables tbl 
            ON SCHEMA_NAME(fk.schema_id) = tbl.SchemaName 
                AND OBJECT_NAME(fc.referenced_object_id) = tbl.Name
)
select
    quotename(fk.ConstraintName) AS ConstraintName
    ,quotename(fk.SchemaName) + '.' + quotename(fk.TableName) AS TableName
    ,quotename(fk.SchemaName) + '.' + quotename(fk.ReferencedTableName) AS ReferencedTableName
    ,replace(fk.delete_referential_action_desc, '_', ' ') AS DeleteAction
    ,replace(fk.update_referential_action_desc, '_', ' ') AS UpdateAction
    ,STUFF((
        SELECT ',' + quotename(fk2.ColumnName)
        FROM foreignKeys fk2 
        WHERE fk2.ConstraintName = fk.ConstraintName
        ORDER BY fk2.constraint_column_id
        FOR XML PATH(''),TYPE
    ).value('.','VARCHAR(MAX)'),1,1,'') AS ColumnNames
    ,STUFF((
        SELECT ',' + quotename(fk2.ReferencedColumnName)
        FROM foreignKeys fk2 
        WHERE fk2.ConstraintName = fk.ConstraintName
        ORDER BY fk2.constraint_column_id
        FOR XML PATH(''),TYPE
    ).value('.','VARCHAR(MAX)'),1,1,'') AS ReferencedColumnNames
into #FKs
from foreignKeys fk
GROUP BY fk.SchemaName, fk.ConstraintName, fk.TableName, fk.ReferencedTableName, fk.delete_referential_action_desc, fk.update_referential_action_desc


-- Drop FKs
select 
    identity(int,1,1) as ID,
    'ALTER TABLE ' + fk.TableName + ' DROP CONSTRAINT ' + fk.ConstraintName AS script
into #scripts
from #FKs fk

-- Truncate 
insert into #scripts
select distinct 
    'TRUNCATE TABLE ' + fk.TableName AS script
from #FKs fk

-- Recreate
insert into #scripts
select 
    'ALTER TABLE ' + fk.TableName + 
    ' WITH CHECK ADD CONSTRAINT ' + fk.ConstraintName + 
    ' FOREIGN KEY ('+ fk.ColumnNames +')' + 
    ' REFERENCES ' + fk.ReferencedTableName +' ('+ fk.ReferencedColumnNames +')' +
    ' ON DELETE ' + fk.DeleteAction + ' ON UPDATE ' + fk.UpdateAction AS script
from #FKs fk


DECLARE @script nvarchar(MAX);

DECLARE curScripts CURSOR FOR 
    select script
    from #scripts
    order by ID

OPEN curScripts

WHILE 1=1 BEGIN
    FETCH NEXT FROM curScripts INTO @script
    IF @@FETCH_STATUS != 0 BREAK;

    print @script;
    IF @Debug = 0
        EXEC (@script);
END
CLOSE curScripts
DEALLOCATE curScripts


drop table #scripts
drop table #FKs
drop table #tables


COMMIT TRANSACTION TruncateTrans;
1
GhotiPhud

Si aucune de ces réponses ne fonctionne comme dans mon cas, procédez comme suit:

  1. Déposer des contraintes
  2. Définir toutes les valeurs pour autoriser les valeurs NULL
  3. Table tronquée
  4. Ajouter des contraintes qui ont été abandonnées.

Bonne chance!

1
G Jeny Ramirez

Si vous le faites à n'importe quelle fréquence, même sur un calendrier, je je n'utiliserais absolument pas, sans équivoque, une instruction DML. Le coût d'écriture dans le journal des transactions est trop élevé et il est ridicule de placer toute la base de données en mode de récupération SIMPLE pour tronquer une table.

La meilleure façon, malheureusement, est la manière difficile ou laborieuse. Cet être:

  • Déposer des contraintes
  • Table tronquée
  • Recréer des contraintes

Mon processus pour ce faire implique les étapes suivantes:

  1. Dans SSMS, cliquez avec le bouton droit sur la table en question, puis sélectionnez Afficher les dépendances.
  2. Prenez note des tables référencées (le cas échéant)
  3. De retour dans l'explorateur d'objets, développez le nœud Keys et notez les clés étrangères (le cas échéant).
  4. Lancer le script (déposer/tronquer/recréer)

Des scripts de cette nature devraient être exécutés dans un bloc begin tran et commit tran.

0
pimbrouwers

Supprimer puis réinitialiser l'incrémentation automatique:

delete from tablename;

puis

ALTER TABLE tablename AUTO_INCREMENT = 1;
0
mwafi

Bon sang - tellement de réponses ... et je vais risquer d'en ajouter une autre ... Il me fallait quelque chose pour supprimer périodiquement un sous-ensemble de tableaux dans les bases de données de production, et à peu près toutes les réponses existantes présentaient un problème qui ne convenait pas à mes préoccupations ... qui étaient:

  • Je devais gérer des contraintes portant des noms tels que ThisTable.to.ThatTable
  • La liste des tables peut s'étendre sur plusieurs schémas
  • La liste des tables à tronquer peut varier - j'ai donc besoin d'une entrée
  • Il y a des clés étrangères avec on delete cascade et d'autres actions
  • Si, après avoir tronqué, je ne peux pas restaurer les clés étrangères, je dois revenir en arrière.
  • Je dois limiter la possibilité de supprimer/rajouter des clés étrangères aux tables concernées.
  • Il me faut au moins des contrôles de cohérence minimum lorsque nous demandons aux clients de gérer ce gâchis.
  • Je veux que les clients courent un et si avant de le faire pour de vrai
  • Je ne peux pas me permettre delete table pour plusieurs raisons.

Je voulais aussi quelque chose de plus facile à gérer et à comprendre que certains des scripts que j'ai étudiés. Je ne sais pas ... Je suppose que c'est subjectif. Vous pouvez détester mon style de codage.

En tout état de cause, il effectue un pré-traitement en sélectionnant certaines grandes variables 'nvarchar(max): une pour supprimer les clés étrangères, une pour effectuer les déclencheurs de désactivation/tronquer la table/activer les déclencheurs et enfin, une pour restaurer les clés étrangères. Ensuite, il imprime ou exécute les résultats.

Il s'attend à ce qu'il y ait un type de table défini par l'utilisateur en tant qu'argument d'entrée. Nous avons une telle chose dans toutes nos bases de données. Ce type est défini comme ceci:

create type dbo.NamesType as table( Name sysname )

Pour l'appeler sur certaines tables à jeter de plusieurs schémas avec des noms de contraintes étranges (et peut-être une entrée malicieuse), voici l'essentiel de mon test harnais:

create schema nop authorization dbo
go
  create schema gak authorization dbo
  go
    create table nop.a(one int primary key)
    create table gak.b(one int constraint [b.to.a] foreign key references nop.a)
    go
      --> do the damage...
      declare @t dbo.NamesType
      insert @t values ('nop.a'),('gak.b') --,(';drop database'),('no.bobby.tables')
      execute dbo.TruncateTables @t --, 0
    go
    drop table gak.b
    drop table nop.a
  go
  drop schema gak
go
drop schema nop

... qui imprime:

alter table gak.b drop constraint [b.to.a];
alter table nop.a disable trigger all;
truncate table nop.a;
alter table nop.a enable trigger all;
alter table gak.b disable trigger all;
truncate table gak.b;
alter table gak.b enable trigger all;
alter table gak.b add constraint [b.to.a] foreign key ( one ) references nop.a( one );

Et enfin, voici la pelote de laine. J'espère que c'est quelqu'un quelques bons ;-)

create procedure dbo.TruncateTables
  @tableNames dbo.NamesType readonly, --> Note: create type dbo.NamesType as table( Name sysname )
  @printOnly bit = 1 --> by default, it only prints...you have to explicly set to 0 to get it to do the damage
as 
begin

  set nocount on

  declare 
    @dropFKsSql nvarchar( max ) = N'',
    @addFKsSql nvarchar( max ) = N'',
    @truncateSql nvarchar( max ) = N'',
    @cr nvarchar( 2 ) = nchar( 13 ) + nchar( 10 ),
    @numberOfInputs int,
    @numberOfEligible int

  declare
    @truncatableTables table( object_id int not null )

  insert @truncatableTables
  select t.object_id 
  from
    @tableNames n 
    inner join
    sys.tables t
    on
      object_id( n.Name ) = t.object_id

  select @numberOfInputs = count(*) from @tableNames
  select @numberOfEligible = count(*) from @truncatableTables
  if ( @numberOfEligible < @numberOfInputs )
  begin
    raiserror( 'At least one of the inputs is not a table in this database', 16, 1 )
    return 1
  end

  select 
    @truncateSql += 
      N'alter table ' + schema_name(t.schema_id) + N'.' + t.name + N' disable trigger all;' + @cr +
      N'truncate table ' + schema_name(t.schema_id) + N'.' + t.name + N';' + @cr +
      N'alter table ' + schema_name(t.schema_id) + N'.' + t.name + N' enable trigger all;' + @cr 
  from 
    sys.tables t
  where 
    t.object_id in ( select object_id from @truncatableTables )

  select 
    @dropFKsSql +=  
      N'alter table '+ schema_name(from_table.schema_id) + N'.' + from_table.name + 
      N' drop constraint [' + fk.name + N']'+
      N';' + @cr,
    @addFKsSql +=
      N'alter table '+ schema_name(from_table.schema_id) + N'.' + from_table.name + 
      N' add constraint [' + fk.name + N'] foreign key ( ' + fk_cols.from_columns + N' ) references ' + 
      schema_name(to_table.schema_id) + N'.' + to_table.name + N'( ' + fk_cols.to_columns + N' )' + 
      case (fk.delete_referential_action) 
      when 1 then N' on delete cascade'  
      when 2 then N' on delete set null'
      when 3 then N' on delete set default'
      else N'' 
      end + 
      case (fk.update_referential_action) 
      when 1 then N' on update cascade'  
      when 2 then N' on update set null'
      when 3 then N' on update set default'
      else N'' 
      end + 
      N';' + @cr
  from 
    sys.foreign_keys fk
    inner join
    sys.objects from_table
    on
      fk.parent_object_id = from_table.object_id
    inner join
    sys.objects to_table
    on
      fk.referenced_object_id = to_table.object_id
    inner join
    (
      select
        o.constraint_object_id,
        stuff( (
          select N',' + col_name(fkc.parent_object_id, fkc.parent_column_id)
          from sys.foreign_key_columns fkc
          where fkc.constraint_object_id = o.constraint_object_id
          order by fkc.constraint_column_id  --> has to be same order in to_columns (below)
          for xml path( '' )
        ), 1, 1, N'' ) as from_columns, 
        stuff( (
          select N',' + col_name(fkc.referenced_object_id, fkc.referenced_column_id)
          from sys.foreign_key_columns fkc
          where fkc.constraint_object_id = o.constraint_object_id
          order by fkc.constraint_column_id --> has to be in same order as from_colums (above)
          for xml path( '' )
        ), 1, 1, N'' ) as to_columns
      from 
        sys.foreign_key_columns o
      where 
        o.constraint_column_id = 1 --> just need a representative row
      group by 
        constraint_object_id
    ) as fk_cols
    on
      fk.object_id = fk_cols.constraint_object_id
    where
      fk.referenced_object_id in ( select object_id from @truncatableTables ) --> limits scope

  print @dropFKsSql
  print @truncateSql
  print @addFKsSql

  if ( @printOnly = 0 )
  begin 
    begin transaction  --> won't have this luxury in Azure
      execute sp_executesql @statement = @dropFKsSql
      execute sp_executesql @statement = @truncateSql
      execute sp_executesql @statement = @addFKsSql
      print 'Got this far, and it didn''t blow up!'
    commit transaction
  end

end

Dans Azure, vous ne pouvez pas exécuter truncate table dans une transaction (grrrr ....). Dans un tel environnement, je devrais m'assurer de manière proactive qu'il existe des enregistrements dans des clés étrangères qui ne participent pas à la troncature. Pas désespérément compliqué, mais pas encore nécessaire pour moi.

0
Clay

Dans SSMS, j'avais un diagramme ouvert montrant la clé. Après avoir supprimé la clé et tronqué le fichier, j'ai actualisé puis concentré sur le diagramme et créé une mise à jour en effaçant puis en restaurant une zone d'identité. L'enregistrement du diagramme a fait apparaître une boîte de dialogue Enregistrer, plus qu'une boîte de dialogue "Des modifications ont été apportées à la base de données alors que vous travailliez", cliquez sur Oui pour restaurer la clé et la restaurer à partir de la copie verrouillée dans le diagramme. 

0
user2584621