【问题标题】:Enable constraint启用约束
【发布时间】:2011-04-21 16:39:01
【问题描述】:

我有一个包含许多约束的庞大数据库,我更新了一些数据禁用约束,我只想重新启用它们。

我用过下面的查询

EXEC sp_msforeachtable 'ALTER TABLE MyTable NOCHECK CONSTRAINT all' 
exec sp_msforeachtable @command1='print ''MyTable''', @command2='ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT all'

但是当我运行启用查询时,由于数据量很大,它需要很长时间。需要1个多小时。

我只想以正确的方式快速启用它而不会出现任何问题。

请指导我如何克服这个问题。

【问题讨论】:

    标签: sql sql-server tsql sql-server-2000


    【解决方案1】:

    以“正确的方式,没有任何问题”来做这件事需要时间。您可以通过省略 WITH CHECK 选项来提高速度,但是您的数据库中最终会出现 untrusted constraints,因此我不推荐该选项。

    【讨论】:

    • 感谢您的快速回复。我需要让它恢复到正常阶段。我不想搞砸任何事情。由于启用时检查现有数据,我认为启用需要时间。我们是否可以选择禁用数据验证并立即启用?
    • @user459295:正如我所说,更快的选择是省略WITH CHECK,这会产生后果。
    • 我不想知道不受信任的约束的事实。我能够快速运行删除 WITH CHECK 选项。它忽略了数据验证。
    • @user459295:阅读我在答案中包含的链接中的文章,以了解不受信任的约束的影响。
    【解决方案2】:

    使用 sp_msforeachtable 按顺序运行语句。很可能将事情分散到几个线程上会大大加快这个过程。使用 Service Broker,这可以在 SQL 代码中完成。

    (以下是从内存中编写的代码,我没有可用于测试的 SQL Server。因此其中可能存在一些错误。只要有可用的服务器,我就会更新。)

    首先创建一个激活程序

      CREATE PROC p_enable_constraints
      AS
         DECLARE @handle UNIQUEIDENTIFIER
               , @message sysname
               , @sql nvarchar(max)
    
         WHILE 1=1
           BEGIN
             BEGIN TRAN
             WAITFOR ( RECEIVE TOP(1) @handle = conversation_handle,
                                  @message = message_body
                       FROM ConstraintQueueReceive), TIMEOUT 1000;
            IF @@rowcount = 0
              begin
                rollback
                break;
              end
    
            set @sql = N'ALTER TABLE ' + quotename(@message) + N' WITH CHECK CONSTRAINT ALL'
            exec (@sql)
            if @@error <> 0
              begin
                rollback tran
                break
              end 
            COMMIT TRANSACTION
          END
        RETURN(0)
       GO
    

    设置队列和服务以发送和接收消息

      CREATE QUEUE ConstraintQueueSend
      CREATE SERVICE ConstraintServiceSend
    
      CREATE QUEUE ConstraintQueueReceive 
        WITH STATUS = ON, ACTIVATION (PROCEDURE_NAME = p_enable_constraints
                                     , MAX_QUEUE_READERS = 8
                                     , EXECUTE AS SELF);
    
      CREATE SERVICE [ConstraintServiceReceive] ON QUEUE ConstraintQueueReceive 
    

    现在创建一个为每个表提交消息的过程

      CREATE PROC p_submit_enable_constraints_message @object sysname
      AS
        DECLARE @handle UNIQUEIDENTIFIER;
        DECLARE @message sysname;
    
        BEGIN TRANSACTION;
        BEGIN DIALOG @handle FROM SERVICE [ConstraintServiceSend] 
                               TO SERVICE [ConstraintServiceReceive]
        WITH ENCRYPTION = OFF;
        SEND ON CONVERSATION @handle (@object);
    
        COMMIT TRANSACTION;
    
        END CONVERSATION @handle
        GO
    

    最后我们可以调用该过程来将它提交到队列中。

       EXEC sp_msforeachtable 'EXEC p_submit_enable_constraints_message N''?'''
    

    理想情况下,alter table 的结果会被捕获并发回。

    【讨论】:

      【解决方案3】:

      要检查的一件事是您是否在约束的两侧都有索引(在正常操作期间这是一个好主意,顺便说一句)。为了明确这一点,假设您有两个表(tbl_a 和 tbl_b),它们之间有一个 FK 约束(假设它是 col_c)。您需要一个索引,其中 tbl_a 和 tbl_b 上的前导列是 col_c ,这样它就不必对没有此类索引的表进行表扫描。

      此外,如果您需要在中间停下来,单独检查每个约束(而不是“检查所有约束”)将为您提供更好的粒度。

      【讨论】:

      • 看起来很糟糕,但你能告诉我一种方法来跟踪与表相关的每个约束吗?然后我知道要单独禁用或启用什么。
      • @user459295:约束在 sys.foreign_keys 中。需要注意的是,name列是约束的名称,parent_object_id是FK所属表的object_id。从那里,您应该能够生成必要的 DDL 语句。另一种方法是 powershell 或您最喜欢的 SMO 使用方式。但那是一匹不同颜色的马。
      猜你喜欢
      • 1970-01-01
      • 2017-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-14
      • 2015-09-22
      • 2010-12-26
      • 2020-10-02
      相关资源
      最近更新 更多