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

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

  • 相关阅读:
    RN-Android构建失败:Caused by: org.gradle.api.ProjectConfigurationException: A problem occurred configuring root project 'AwesomeProject'.
    Android更新包下载成功后不出现安装界面
    真机调试: The application could not be installed: INSTALL_FAILED_TEST_ONLY
    react native 屏幕尺寸转换
    Android Studio生成签名文件,自动签名,以及获取SHA1和MD5值
    React Native安卓真机调试
    git提交代码报错Permission denied, please try again
    The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.
    命令行设置快捷命令
    Linux 常用指令
  • 原文地址:https://www.cnblogs.com/njl041x/p/3445637.html
Copyright © 2011-2022 走看看