實際開發中,清空數據庫表數據常遇到三種遞進的需求:
最簡單高效的方法是使用 TRUNCATE TABLE,它比 DELETE 更快、記錄日誌更少、自動重置標識列。
DECLARE @tableName VARCHAR(128), @sql NVARCHAR(MAX);
DECLARE cur CURSOR FOR
SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';
OPEN cur;
FETCH NEXT FROM cur INTO @tableName;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = N'TRUNCATE TABLE ' + QUOTENAME(@tableName);
EXEC sp_executesql @sql;
FETCH NEXT FROM cur INTO @tableName;
END
CLOSE cur; DEALLOCATE cur;
DECLARE @sql NVARCHAR(MAX) = N'';
SELECT @sql += N'TRUNCATE TABLE ' + QUOTENAME(TABLE_NAME) + N';'
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';
EXEC sp_executesql @sql;
關鍵點:TRUNCATE TABLE 本身就會重置標識列種子值。若因約束無法使用 TRUNCATE,需手動重置:
-- 先 DELETE 再重置
DELETE FROM TableName;
DBCC CHECKIDENT('TableName', RESEED, 0); -- 下一條插入從 1 開始
外鍵約束阻止 TRUNCATE,必須按拓撲順序操作:
SET NOCOUNT ON;
DECLARE @tableName SYSNAME, @sql NVARCHAR(MAX);
-- 第一輪:處理無外鍵或僅有主鍵的表(可 TRUNCATE)
WHILE EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.TABLES T
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
ON T.TABLE_NAME = TC.TABLE_NAME AND TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
WHERE T.TABLE_TYPE = 'BASE TABLE'
AND T.TABLE_NAME NOT IN ('dtproperties', 'sysconstraints', 'syssegments')
AND (TC.CONSTRAINT_TYPE IS NULL OR TC.CONSTRAINT_TYPE = 'PRIMARY KEY')
AND T.TABLE_NAME > @tableName
)
BEGIN
SELECT @tableName = MIN(T.TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES T
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
ON T.TABLE_NAME = TC.TABLE_NAME AND TC.CONSTRAINT_TYPE = 'FOREIGN KEY'
WHERE T.TABLE_TYPE = 'BASE TABLE'
AND T.TABLE_NAME NOT IN ('dtproperties', 'sysconstraints', 'syssegments')
AND (TC.CONSTRAINT_TYPE IS NULL OR TC.CONSTRAINT_TYPE = 'PRIMARY KEY')
AND T.TABLE_NAME > @tableName;
SET @sql = N'TRUNCATE TABLE ' + QUOTENAME(@tableName);
PRINT @sql; EXEC sp_executesql @sql;
END
-- 第二輪:處理有外鍵被引用的表(必須 DELETE + RESEED)
SET @tableName = '';
WHILE EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.TABLES T
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
ON T.TABLE_NAME = TC.TABLE_NAME AND TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
WHERE T.TABLE_TYPE = 'BASE TABLE'
AND T.TABLE_NAME <> 'dtproperties'
AND T.TABLE_NAME > @tableName
)
BEGIN
SELECT @tableName = MIN(T.TABLE_NAME)
FROM INFORMATION_SCHEMA.TABLES T
JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
ON T.TABLE_NAME = TC.TABLE_NAME AND TC.CONSTRAINT_TYPE = 'PRIMARY KEY'
WHERE T.TABLE_TYPE = 'BASE TABLE'
AND T.TABLE_NAME <> 'dtproperties'
AND T.TABLE_NAME > @tableName;
-- DELETE 數據
SET @sql = N'DELETE FROM ' + QUOTENAME(@tableName);
PRINT @sql; EXEC sp_executesql @sql;
-- 檢查並重置標識列
IF EXISTS (
SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @tableName
AND COLUMNPROPERTY(OBJECT_ID(QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(@tableName)),
COLUMN_NAME, 'IsIdentity') = 1
)
BEGIN
SET @sql = N'DBCC CHECKIDENT(' + QUOTENAME(@tableName, '''') + N', RESEED, 0)';
PRINT @sql; EXEC sp_executesql @sql;
END
END
SET NOCOUNT OFF;
若表數量較多且結構穩定,可臨時禁用所有約束:
-- 禁用所有約束
EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL';
-- 執行 TRUNCATE
EXEC sp_msforeachtable 'TRUNCATE TABLE ?';
-- 重新啟用約束
EXEC sp_msforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL';
注意:sp_msforeachtable 為非文檔化存儲過程,生產環境謹慎使用;啟用約束時會驗證現有數據,若數據違反約束會報錯。
PRINT 輸出執行的語句,便於審計與排查