【问题标题】:Cannot drop index because of foreign key constraint enforcement由于执行外键约束,无法删除索引
【发布时间】:2014-09-07 19:01:05
【问题描述】:

我无法删除索引,因为某些表将它用作外键

消息 3723,级别 16,状态 6,第 1 行 允许在索引“tbl1.ix_cox”上。它被用于 FOREIGN KEY 约束执行。

我尝试在删除之前先禁用索引

ALTER INDEX ix_cox On tbl1
DISABLE
Go

但还是放不下。

我真的需要删除那些使用该索引的表上的外键吗?因为它大约有 30 张桌子。

【问题讨论】:

  • 创建您的索引是为了优化和强制执行主键或唯一键。可以创建与这些列相关的另一个表中的外键,因为存在此主键或唯一状态表明列值是唯一的并且正确标识表中的 1 行。删除索引会破坏这种一致性,因此您别无选择,只能先删除相关的外键,然后再删除索引。出于同样的原因,在您建立另一个主键或唯一主键之前,您将无法重新创建其他外键。
  • 有点晚了,但请看下面我的 vwFK 视图,以便轻松生成处理大量引用外键的表所需的脚本。
  • 这个问题的答案可能有助于找到所有外键在哪里(如果你不确定)stackoverflow.com/questions/18707037/…

标签: sql sql-server-2008 tsql indexing


【解决方案1】:

如果创建索引时定义了 PRIMARY KEY 或 UNIQUE 约束 (https://docs.microsoft.com/en-us/sql/t-sql/statements/drop-index-transact-sql?view=sql-server-2017),您将遇到同样的异常。

在这种情况下,简单的解决方案是改用 ALTER-TABLE-命令:

ALTER TABLE tbl1 DROP CONSTRAINT ix_cox

【讨论】:

  • 为#MeToo 解决了这个问题!
【解决方案2】:

恐怕这是您唯一的选择。您必须删除所有引用表的外键约束,并且您不能重新创建外键约束,直到您在删除索引的表上指定另一个唯一索引。

【讨论】:

    【解决方案3】:

    早安,

    这是一个完整的例子:

    让我们先创建表并插入一些示例数据

    use tempdb
    GO
    
    -- NOTE! 
    --    This sample code presents a POOR CODING where the user
    --    does not explicitly named the objects
    --    or explicitly create CLUSTERED INDEX
    
    DROP TABLE IF EXISTS dbo.Users_tbl;
    DROP TABLE IF EXISTS dbo.Categories_tbl;
    GO
    
    CREATE TABLE dbo.Categories_tbl(
        CategoryID INT IDENTITY(1,1) PRIMARY KEY
        , CategoryName NVARCHAR(100)
    )
    GO 
    -- find the CLUSTERED INDEX created automatically for us
    SELECT * FROM sys.indexes
        WHERE object_id = OBJECT_ID('Categories_tbl')
    GO
    -- you can notice that by default the PRIMARY KEY become CLUSTERED INDEX
    -- If we did not configure a different CLUSTERED INDEX
    -- In my case the automatic name was: PK__Categori__19093A2BAE0EA4C3
    
    -- Let's create the secondary table
    CREATE TABLE dbo.Users_tbl(
        UserID INT IDENTITY(1,1) PRIMARY KEY
        , UserName NVARCHAR(100)
        , CategoryID INT
        , FOREIGN KEY (CategoryID) REFERENCES Categories_tbl(CategoryID)
    )
    GO 
    
    -- Insert sample data
    INSERT Categories_tbl (CategoryName) VALUES ('a'),('b')
    GO
    INSERT Users_tbl(UserName,CategoryID)
        VALUES ('a',1),('b',1)
    GO
    
    SELECT * FROM Categories_tbl
    SELECT * FROM Users_tbl
    GO
    

    从主键中删除聚集索引

    如果我们尝试删除 PK 的 DROP INDEX 则会收到此错误:

    索引上不允许使用显式 DROP INDEX...它正在用于 PRIMARY KEY 约束强制执行。

    解决方案是删除FK,删除PK,使用NONCLUSTERED索引而不是CLUSTERED创建新PK,然后创建FK

    /************************************************  */
    /********* REMOVE CLUSTERED INDEX from PRIMARY KEY */
    /************************************************  */
    ------------------------------------------------------ 
    -- Step 1: DROP the CONSTRAINTs
    ------------------------------------------------------ 
    ---- Get FOREIGN KEY name
    SELECT DISTINCT OBJECT_NAME(f.constraint_object_id)
    FROM sys.foreign_key_columns f
    LEFT JOIN sys.indexes p ON p.object_id = f.referenced_object_id
        WHERE p.object_id = OBJECT_ID('Categories_tbl')
    GO
    
    -- DROP FOREIGN KEY
    ALTER TABLE dbo.Users_tbl   
        DROP CONSTRAINT FK__Users_tbl__Categ__59063A47 -- Use the name we found above
    GO
    
    ---- Get PRIMARY KEY name
    SELECT name FROM sys.indexes
        WHERE object_id = OBJECT_ID('Categories_tbl')
    GO
    -- DROP PRIMARY KEY
    ALTER TABLE dbo.Categories_tbl
        DROP CONSTRAINT PK__Categori__19093A2B9F118674 -- Use the name we found above
    GO
    
    
    ------------------------------------------------------ 
    -- Step 2: CREATE new CONSTRAINTs
    ------------------------------------------------------ 
    -- And now we can create new PRIMARY KEY NONCLUSTERED
    --   Since we use PRIMARY KEY We need to have index,
    --   but we do not have to use CLUSTERED INDEX
    --   we can have NONCLUSTERED INDEX
    ALTER TABLE dbo.Categories_tbl
        ADD CONSTRAINT PK_CategoryID PRIMARY KEY NONCLUSTERED (CategoryID);
    GO
    -- Finaly we can create the 
    ALTER TABLE dbo.Users_tbl
        ADD CONSTRAINT FK_Categories_tbl 
        FOREIGN KEY (CategoryID)     
        REFERENCES dbo.Categories_tbl(CategoryID)
    GO
    

    【讨论】:

      【解决方案4】:

      您可以暂时禁用 FK 约束 -> 删除索引 -> 重新启用约束,如

      ALTER TABLE your_fk_table NOCHECK CONSTRAINT constraint_name
      
      drop index ids_name
      
      ALTER TABLE your_fk_table CHECK CONSTRAINT constraint_name
      

      【讨论】:

      • 对我来说同样的错误,即使没有检查约束。
      【解决方案5】:

      好吧,如果您在像 EzLo 这样的 30 张桌子上有一个 FK,那么进行一些自动化肯定会很好。此视图最后两列中的脚本将删除并重新添加您的 FK:

      CREATE VIEW vwFK
      AS
          select *, addFK = 'ALTER TABLE '+FKtable+' WITH ' + case when is_not_trusted = 1 then 'NO' else '' end + 'CHECK'
                              + ' ADD  CONSTRAINT [FK_'+FKtbl+'_'+PKtbl+'] FOREIGN KEY ('+FKcol+') '
                              + ' REFERENCES '+PKtable+' ('+PKcol+')'+' ON UPDATE '+onUpdate+' ON DELETE '+onDelete
                              + case when is_not_for_replication = 1 then ' NOT FOR REPLICATION' else '' end + ';'
                              + case when is_disabled = 1 then '  ALTER TABLE '+FKtable+' NOCHECK CONSTRAINT [FK_'+FKtbl+'_'+PKtbl+'];' else '' end
              ,dropFK = 'ALTER TABLE '+FKtable+' DROP ['+FK+'];'
          from (
              select
                   PKtable    = object_schema_name(f.referenced_object_id)+'.'+object_name(f.referenced_object_id)
                  ,PKtbl      = object_name(f.referenced_object_id)
                  ,PKcol      = pc.name
                  ,FKtable    = object_schema_name(f.parent_object_id)+'.'+object_name(f.parent_object_id)
                  ,FKtbl      = object_name(f.parent_object_id)
                  ,colseq     = fk.constraint_column_id
                  ,FKcol      = fc.name
                  ,FK         = object_name(f.object_id)
                  ,onUpdate   = replace(f.update_referential_action_desc collate SQL_Latin1_General_CP1_CI_AS, '_', ' ')
                  ,onDelete   = replace(f.delete_referential_action_desc collate SQL_Latin1_General_CP1_CI_AS, '_', ' ')
                  ,f.is_disabled
                  ,f.is_not_trusted
                  ,f.is_not_for_replication
              from sys.foreign_key_columns as fk
                  join sys.foreign_keys f on fk.constraint_object_id = f.object_id
                  join sys.columns as fc on f.parent_object_id = fc.object_id and fk.parent_column_id = fc.column_id
                  join sys.columns as pc on f.referenced_object_id = pc.object_id and fk.referenced_column_id = pc.column_id
          ) t
      

      没有保证,但这多年来为我节省了很多的工作。 请记住在运行 dropFK 之前保存 addFK 语句!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-18
        • 2021-05-13
        • 2019-08-17
        • 1970-01-01
        • 2021-09-03
        • 2011-12-29
        • 2012-12-27
        相关资源
        最近更新 更多