zoukankan      html  css  js  c++  java
  • SQLSERVER清空(Truncate)被外键引用的数据表

    前言:我们知道SQLSERVER清空数据表有两种方式Delete和Truncate,当然两者的不同大家也都知道(不清楚的可以MSDN)。不过这个错误“Cannot truncate table  because it is being referenced by a FOREIGN KEY” 相信大家也都遇到过,解决的已解决,未解决的且看下文。

    如何解决

    开始我以为只要将外键Disable掉就可以了,事实证明是没用的。其实MSDN已经明确告诉了我们:

    不能对以下表使用 TRUNCATE TABLE:

    • 由 FOREIGN KEY 约束引用的表。(您可以截断具有引用自身的外键的表。)
    • 参与索引视图的表。
    • 通过使用事务复制或合并复制发布的表。

    对于具有以上一个或多个特征的表,请使用 DELETE 语句。

    TRUNCATE TABLE 不能激活触发器,因为该操作不记录各个行删除

    难道我真的要用Delete吗?可我真的不想用Delete。原因就在于Truncate的优点,MSDN说:

    与 DELETE 语句相比,TRUNCATE TABLE 具有以下优点:

    • 所用的事务日志空间较少。
      DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一个项。TRUNCATE TABLE 通过释放用于存储表数据的数据页来删除数据,并且在事务日志中只记录页释放。
    • 使用的锁通常较少。
      当使用行锁执行 DELETE 语句时,将锁定表中各行以便删除。TRUNCATE TABLE 始终锁定表和页,而不是锁定各行。
    • 如无例外,在表中不会留有任何页。
      执行 DELETE 语句后,表仍会包含空页。(略去例如)

    好了,下面就来说一下解决方法。

    解决方案

    1.使用Delete

    a) 先Delete依赖表(或叫从表)

    b) 再Delete被依赖表(或叫主表)

    2.使用Truncate

    a) 先备份依赖表外键

    b) 删除依赖表外键

    c) Truncate主表

    d) 重新创建依赖表外键

    一段脚本

    其实是一个使用Truncate进行处理的存储过程,思路见上。

      1 USE <YOUR DB>
      2 GO
      3 
      4 CREATE PROCEDURE [dbo].[usp_Truncate_Table]
      5   @TableToTruncate VARCHAR(64)
      6 AS 
      7 
      8 BEGIN
      9 
     10 SET NOCOUNT ON
     11 
     12 --==变量定义
     13 DECLARE @i int
     14 DECLARE @Debug bit
     15 DECLARE @Recycle bit
     16 DECLARE @Verbose bit
     17 DECLARE @TableName varchar(80)
     18 DECLARE @ColumnName varchar(80)
     19 DECLARE @ReferencedTableName varchar(80)
     20 DECLARE @ReferencedColumnName varchar(80)
     21 DECLARE @ConstraintName varchar(250)
     22 
     23 DECLARE @CreateStatement varchar(max)
     24 DECLARE @DropStatement varchar(max)   
     25 DECLARE @TruncateStatement varchar(max)
     26 DECLARE @CreateStatementTemp varchar(max)
     27 DECLARE @DropStatementTemp varchar(max)
     28 DECLARE @TruncateStatementTemp varchar(max)
     29 DECLARE @Statement varchar(max)
     30 
     31  SET @Debug = 0--(0:将执行相关语句|1:不执行语句)
     32  SET @Recycle = 0--(0:不创建/不清除存储表|1:将创建/清理存储表)
     33  set @Verbose = 1--(1:每步执行均打印消息|0:不打印消息)
     34 
     35  SET @i = 1
     36     SET @CreateStatement = 'ALTER TABLE [dbo].[<tablename>]  WITH NOCHECK ADD  CONSTRAINT [<constraintname>] FOREIGN KEY([<column>]) REFERENCES [dbo].[<reftable>] ([<refcolumn>])'
     37     SET @DropStatement = 'ALTER TABLE [dbo].[<tablename>] DROP CONSTRAINT [<constraintname>]'
     38     SET @TruncateStatement = 'TRUNCATE TABLE [<tablename>]'
     39 
     40 -- 创建外键临时表
     41 IF OBJECT_ID('tempdb..#FKs') IS NOT NULL
     42     DROP TABLE #FKs
     43 
     44 -- 获取外键
     45 SELECT ROW_NUMBER() OVER (ORDER BY OBJECT_NAME(parent_object_id), clm1.name) as ID,
     46        OBJECT_NAME(constraint_object_id) as ConstraintName,
     47        OBJECT_NAME(parent_object_id) as TableName,
     48        clm1.name as ColumnName, 
     49        OBJECT_NAME(referenced_object_id) as ReferencedTableName,
     50        clm2.name as ReferencedColumnName
     51   INTO #FKs
     52   FROM sys.foreign_key_columns fk
     53        JOIN sys.columns clm1 ON fk.parent_column_id = clm1.column_id AND fk.parent_object_id = clm1.object_id
     54        JOIN sys.columns clm2 ON fk.referenced_column_id = clm2.column_id AND fk.referenced_object_id= clm2.object_id
     55  --WHERE OBJECT_NAME(parent_object_id) not in ('//tables that you do not wont to be truncated')
     56  WHERE OBJECT_NAME(referenced_object_id) = @TableToTruncate
     57  ORDER BY OBJECT_NAME(parent_object_id)
     58 
     59 -- 外键操作(删除|重建)表
     60 IF Not EXISTS(SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Internal_FK_Definition_Storage')
     61 BEGIN
     62     IF @Verbose = 1
     63         PRINT '1. 正在创建表(Internal_FK_Definition_Storage)...'
     64     CREATE TABLE [Internal_FK_Definition_Storage] 
     65     (
     66         ID int not null identity(1,1) primary key,
     67         FK_Name varchar(250) not null,
     68         FK_CreationStatement varchar(max) not null,
     69         FK_DestructionStatement varchar(max) not null,
     70         Table_TruncationStatement varchar(max) not null
     71     ) 
     72 END 
     73 ELSE
     74 BEGIN
     75     IF @Recycle = 0
     76     BEGIN
     77         IF @Verbose = 1
     78         PRINT '1. 正在清理表(Internal_FK_Definition_Storage)...'
     79         TRUNCATE TABLE [Internal_FK_Definition_Storage]    
     80     END
     81     ELSE
     82         PRINT '1. 正在清理表(Internal_FK_Definition_Storage)...'
     83 END
     84 
     85 IF @Recycle = 0
     86 BEGIN
     87     IF @Verbose = 1
     88         PRINT '2. 正在备份外键定义...'           
     89     WHILE (@i <= (SELECT MAX(ID) FROM #FKs))
     90     BEGIN
     91         SET @ConstraintName = (SELECT ConstraintName FROM #FKs WHERE ID = @i)
     92         SET @TableName = (SELECT TableName FROM #FKs WHERE ID = @i)
     93         SET @ColumnName = (SELECT ColumnName FROM #FKs WHERE ID = @i)
     94         SET @ReferencedTableName = (SELECT ReferencedTableName FROM #FKs WHERE ID = @i)
     95         SET @ReferencedColumnName = (SELECT ReferencedColumnName FROM #FKs WHERE ID = @i)
     96 
     97         SET @DropStatementTemp = REPLACE(REPLACE(@DropStatement,'<tablename>',@TableName),'<constraintname>',@ConstraintName)
     98         SET @CreateStatementTemp = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(@CreateStatement,'<tablename>',@TableName),'<column>',@ColumnName),'<constraintname>',@ConstraintName),'<reftable>',@ReferencedTableName),'<refcolumn>',@ReferencedColumnName)
     99         SET @TruncateStatementTemp = REPLACE(@TruncateStatement,'<tablename>',@TableName) 
    100 
    101         INSERT INTO [Internal_FK_Definition_Storage]
    102         SELECT @ConstraintName, @CreateStatementTemp, @DropStatementTemp, @TruncateStatementTemp
    103         
    104         SET @i = @i + 1
    105         
    106         IF @Verbose = 1
    107             PRINT '  > 已备份外键:[' + @ConstraintName + '] 所属表: [' + @TableName + ']'
    108     END   
    109 END   
    110 ELSE 
    111     PRINT '2. 正在备份外键定义...'
    112 
    113 IF @Verbose = 1
    114     PRINT '3. 正在删除外键...'
    115 BEGIN TRAN    
    116 BEGIN TRY
    117 SET @i = 1
    118 WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
    119 BEGIN
    120     SET @ConstraintName = (SELECT FK_Name FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    121     SET @Statement = (SELECT FK_DestructionStatement FROM [Internal_FK_Definition_Storage] WITH (NOLOCK) WHERE ID = @i)
    122     IF @Debug = 1 
    123         PRINT @Statement
    124     ELSE
    125         EXEC(@Statement)
    126     SET @i = @i + 1
    127     IF @Verbose = 1
    128         PRINT '  > 已删除外键:[' + @ConstraintName + ']'
    129 END     
    130 
    131 IF @Verbose = 1
    132     PRINT '4. 正在清理数据表...'
    133 --先清除该外键所在表(由于外键所在表仍可能又被其他外键所引用,因此需要循环递归处理)(注:本处理未实现)
    134 --请不要使用下面注释代码
    135 /*    
    136 SET @i = 1
    137 WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
    138 BEGIN
    139     SET @Statement = (SELECT Table_TruncationStatement FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    140     IF @Debug = 1 
    141         PRINT @Statement
    142     ELSE
    143         EXEC(@Statement)
    144     SET @i = @i + 1
    145     IF @Verbose = 1
    146         PRINT '  > ' + @Statement
    147 END
    148 */
    149 
    150 IF @Debug = 1 
    151     PRINT 'TRUNCATE TABLE [' + @TableToTruncate + ']'
    152 ELSE
    153     EXEC('TRUNCATE TABLE [' + @TableToTruncate + ']')
    154 IF @Verbose = 1
    155     PRINT '  > 已清理数据表[' + @TableToTruncate + ']'
    156     
    157 IF @Verbose = 1
    158     PRINT '5. 正在重建外键...'
    159 SET @i = 1
    160 WHILE (@i <= (SELECT MAX(ID) FROM [Internal_FK_Definition_Storage]))
    161 BEGIN
    162     SET @ConstraintName = (SELECT FK_Name FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    163     SET @Statement = (SELECT FK_CreationStatement FROM [Internal_FK_Definition_Storage] WHERE ID = @i)
    164     IF @Debug = 1 
    165         PRINT @Statement
    166     ELSE
    167         EXEC(@Statement)
    168     SET @i = @i + 1
    169     IF @Verbose = 1
    170     PRINT '  > 已重建外键:[' + @ConstraintName + ']'
    171 END
    172     COMMIT
    173 END TRY
    174 BEGIN CATCH
    175     ROLLBACK 
    176     PRINT '出错信息:'+ERROR_MESSAGE()
    177 END CATCH
    178 IF @Verbose = 1
    179     PRINT '6. 处理完成!'
    180 END
    View Code

    如何使用

    例子说明:清空整个数据库

     1 USE <YOUR DB>
     2 GO
     3 
     4 --==创建临时表
     5 IF(OBJECT_ID('TEMPDB..#TEMP')IS NOT NULL)
     6     DROP TABLE #TEMP
     7 
     8 --==读取数据库表
     9 SELECT SN=ROW_NUMBER()OVER(ORDER BY [name]ASC),TableName=[name]
    10 INTO #TEMP
    11 FROM sys.tables 
    12 WHERE [name]<>'Internal_FK_Definition_Storage'
    13 
    14 --SELECT * FROM #TEMP
    15 
    16 --==开始处理
    17 DECLARE @ROWS INT 
    18 SELECT @ROWS=MAX(SN)FROM #TEMP
    19 DECLARE @I INT
    20 SET @I=1
    21 DECLARE @TableName VARCHAR(64)
    22 WHILE(@I<=@ROWS)
    23 BEGIN
    24     IF(EXISTS(SELECT 1 FROM #TEMP WHERE SN=@I))
    25     BEGIN
    26         SELECT @TableName=TableName FROM #TEMP WHERE SN=@I
    27         EXEC [dbo].[usp_Truncate_Table] @TableToTruncate = @TableName
    28     END
    29     SET @TableName=N''
    30     SET @I=@I+1
    31 END
    View Code

    结束语:文章无甚深浅,止乎于分享。如有错误,还望斧正。

  • 相关阅读:
    C# 面向对象系列笔记(三)
    C# 面向对象系列笔记(二)
    分享一下: C# 面向对象系列笔记(一) ,希望对大家有所帮助
    新人报到
    VUE+ELEMENT-UI的后台项目封装组件--查询form的封装
    vue+element-ui的后台项目封装组件--面包屑的封装
    数组对象循环格式化时间撮的转换
    VUE+ELEMENT-UI的后台项目封装组件--table的封装
    css的样式问题
    vue项目兼容es6语法跟IE浏览器
  • 原文地址:https://www.cnblogs.com/njl041x/p/3445637.html
Copyright © 2011-2022 走看看