由于外键约束强制执行,无法删除索引

时间:2014-07-17 03:39:12

标签: sql sql-server-2008 tsql indexing

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

  

消息3723,级别16,状态6,行1显式DROP INDEX不是   允许索引'tbl1.ix_cox'。它被用于FOREIGN KEY   约束执行。

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

ALTER INDEX ix_cox On tbl1
DISABLE
Go

但仍然无法放弃它。

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

5 个答案:

答案 0 :(得分:6)

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

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

ALTER TABLE tbl1 DROP CONSTRAINT ix_cox

答案 1 :(得分:2)

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

答案 2 :(得分:0)

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

ALTER TABLE your_fk_table NOCHECK CONSTRAINT constraint_name

drop index ids_name

ALTER TABLE your_fk_table CHECK CONSTRAINT constraint_name

答案 3 :(得分:0)

美好的一天,

这是一个完整的例子:

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

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。它正用于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 :(得分:0)

好吧,如果您有30张桌子上的FK(例如EzLo),那么进行一些自动化肯定会很好。该视图最后两列中的脚本将删除并重新添加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语句!