如何使用TSQL截断数据库中的所有表?


204

我有一个数据库测试环境,希望在测试周期开始时重新加载新数据。我对重建整个数据库不感兴趣-只是简单地“重置”数据。

使用TSQL从所有表中删除所有数据的最佳方法是什么?是否存在可以使用的系统存储过程,视图等?我不想为每个表手动创建和维护截断表语句-我希望它是动态的。

Answers:


188

对于SQL 2005,

EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'

2000年2005/2008 年提供更多链接。


62
您无法截断具有外键的表,因此只有在表之间没有外键约束(或它们已被禁用)时,这才起作用。
2008年

1
agreed..i以为因为他特别要求截断表,他已经解决了这个问题与外键..
古勒扎尔的Nazim

@ gulzar-之类的-我对如何处理FK提出了一个单独的问题,但您的回答有其自身的优点。

11
@Sam:不,不会!表中的数据无关紧要。只要存在引用该表的外键约束(甚至是禁用的表),您就无法截断它。
TToni 2010年

3
'EXEC sp_MSForEachTable'DROP TABLE吗?' 工作也很好:)(从数据库中
提取

418

在处理具有外键关系的表中的数据删除时(对于任何经过适当设计的数据库基本上都是这种情况),我们可以禁用所有约束,删除所有数据,然后重新启用约束

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

此处更多有关禁用约束和触发器的信息

如果某些表具有标识列,我们可能需要重新设置它们的种子

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

请注意,RESEED的行为在全新表和先前已从BOL插入一些数据的表之间有所不同:

DBCC CHECKIDENT('table_name',RESEED,newReseedValue)

当前标识值设置为newReseedValue。如果自创建以来没有将任何行插入到表中,则在执行DBCC CHECKIDENT之后插入的第一行将使用newReseedValue作为标识。否则,插入的下一行将使用newReseedValue +1。如果newReseedValue的值小于标识列中的最大值,则在对该表的后续引用上将生成错误消息2627。

感谢Robert指出禁用约束不允许使用截断的事实,必须先删除约束,然后重新创建


33
禁用约束将不允许截断FOREIGN KEY约束所引用的表。FK约束必须删除。如果我对此有误,请回复,但我发现无法避免删除它们。
罗伯特·克莱普尔

1
该语句中不应只存在一个拼写错误的“ Table”关键字EXEC sp_MSForEachTable“ DELETE FROM TABLE?” 正确的版本应该是:EXEC sp_MSForEachTable“ DELETE FROM?”
拉加夫2009年

4
如果您使用的是2008年或更新版本的SSMS,则可能要SET ROWCOUNT 0在脚本的开头添加代码,因为默认设置是将操作限制为500行!您会像我一样遇到令人沮丧的错误,因为并非所有数据实际上都已被删除。
肖恩·汉利

1
这很棒。在我的情况下,我还必须在delete语句之前和之后添加EXEC sp_msforeachtable“ ALTER TABLE?Disable TRIGGER all”和EXEC sp_msforeachtable“ ALTER TABLE?Enable TRIGGER all”。
RobC

2
我最喜欢的答案。但是,为什么您(甚至所有评论者)都将文字SQL字符串括在双引号中?
bitoolean

57

这是数据库擦除脚本的王者之父。它将清除所有表并正确重新设置它们的种子:

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;

享受,但要小心!


2
不幸的是,如果计算了列,则上述命令将失败,因为sp_MSforeachtable显然SET QUOTED_IDENTITY OFF在其主体中(link)。更新:解决方法是添加“ SET QUOTED_IDENTIFIERS on”;每个语句的开头这就提出了这个错误(如提到这里
李英健

1
看来这并没有改变我的身份
tooooooo

48

最简单的方法是

  1. 打开SQL Management Studio
  2. 导航到您的数据库
  3. 右键单击并选择“任务”->“生成脚本”(图1)
  4. 在“选择对象”屏幕上,选择“选择特定对象”选项并选中“表”(图2)
  5. 在下一个屏幕上,选择“高级”,然后将“脚本删除和创建”选项更改为“脚本删除和创建”(图3)
  6. 选择将脚本保存到新的编辑器窗口或文件中,然后根据需要运行。

这将为您提供一个脚本,该脚本可以删除并重新创建所有表,而无需担心调试或是否已包含所有内容。尽管这不仅执行截断,但结果是相同的。请记住,您的自动递增主键将从0开始,而截断表会记住最后分配的值。如果您无权访问PreProd或Production环境中的Management Studio,也可以从代码中执行此操作。

1。

在此处输入图片说明

2。

在此处输入图片说明

3。

在此处输入图片说明


1
如果将其用于具有非常复杂模式的数据库,请注意。我在生产数据库的dev副本上进行了尝试,并将其浪费在架构上,需要重新部署。
Techrocket17年

1
您必须编写要保留的所有内容的脚本
Kenpachi队长

12

仅当表之间没有任何外键关系时,才截断所有表,因为SQL Server不允许您用外键截断表。

替代方法是先确定带有外键的表,然后从中删除,然后再截断不带外键的表。

有关更多详细信息,请参见http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=65341http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72957


1
好点子。没想到。我也许可以先禁用所有约束,然后在数据删除后重新启用它们。

7

我想与MSSQL Server Deveploper或Enterprise一起使用的另一种选择是在创建空架构后立即创建数据库的快照。那时,您可以继续将数据库还原回快照。


不幸的是,您每次都会丢失所有的FULLTEXT索引
克里斯·KL 2014年

6

不要这样!真的,不是一个好主意。

如果知道要截断哪些表,请创建一个将其截断的存储过程。您可以修复订单以避免外键问题。

如果您真的想全部截断它们(例如,可以BCP加载它们),则将很快删除数据库并从头开始创建一个新数据库,这将带来额外的好处,即您可以确切地知道自己的位置。


不错的替代方法。
山姆

3
您的方法存在的问题是,删除表和数据库将导致丢失为不同登录名和架构赋予的所有权限。对于具有大量表的大型数据库来说,重新创建它会很痛苦。
Punit Vora

4

如果要在删除/截断同一数据库内其他表中的数据时将数据保留在特定表(即静态查找表)中,则需要一个循环,其中包含异常。这是我偶然发现此问题时要寻找的东西。

sp_MSForEachTable对我来说似乎是错误的(即,与IF语句不一致的行为),这可能是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

4

截断所有表的最困难部分是删除并重新添加外键约束。

以下查询为与@myTempTable中每个表名称相关的每个约束创建drop&create语句。如果要为所有表生成这些表,则可以简单地使用信息模式来收集这些表名。

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)

然后,我只是复制出要运行的语句-但是您可以花一些开发人员的精力来使用游标动态地运行它们。


3

编写数据库脚本很容易(甚至可能更快),然后只需从脚本中删除并创建它即可。


3

创建一个空的“模板”数据库,进行完整备份。当您需要刷新时,只需使用WITH REPLACE进行还原即可。快速,简单,防弹。而且,如果这里或那里的几个表需要一些基础数据(例如,配置信息,或仅使应用运行的基本信息),它也可以处理。


2

这是这样做的一种方法...可能还有10个更好/更高效的方法,但是听起来这很少进行,所以这里...

得到的名单tables,从sysobjects对这些用游标,然后循环,呼吁sp_execsql('truncate table ' + @table_name)每个iteration


用sql添加了执行此操作的帖子:)因为这也是我一直在寻找的内容。
克里斯·史密斯

1

运行注释掉的部分一次,用要截断的表填充_TruncateList表,然后运行脚本的其余部分。如果您经常这样做,则需要随着时间的流逝清除_ScriptLog表。

如果要处理所有表,则可以修改此表,只需将SELECT名称放入SYS.tables中的INTO #TruncateList中即可。但是,您通常不希望全部完成这些操作。

同样,这将影响数据库中的所有外键,如果对应用程序来说它太过钝,您也可以修改它。不是为了我的目的

/*
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

0

我不明白为什么清除数据比删除并重新创建每个表的脚本更好。

那或保留您的空数据库的备份,并将其恢复到旧数据库


2
原因是删除和重新创建数据库磁盘文件,日志等的开销非常缓慢。考虑在一个不错的单元测试过程中将数据库擦除1000次。
克里斯·KL

0

截断表格之前,必须删除所有外键。使用此脚本 生成最终脚本,以删除并重新创建数据库中的所有外键。请将@action变量设置为“ CREATE”或“ DROP”。


0

从“ INFORMATION_SCHEMA.TABLES”中选择“从'+ TABLE_NAME中删除”,其中TABLE_TYPE ='BASE TABLE'

结果在哪里。

复制并粘贴到查询窗口并运行命令


0

有点晚了,但可能会帮助某人。我有时创建了一个过程,该过程使用T-SQL执行以下操作:

  1. 将所有约束存储在临时表中
  2. 放下所有约束
  3. 截断所有表,除某些表外,不需要截断
  4. 重新创建所有约束。

我列出了它在我的博客在这里

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.