Sql-Server

無法刪除非 PK 索引,因為它在外鍵約束中被引用

  • January 15, 2018

我有一張名為MyTable. 主鍵是一個名為 的標識 int 列MyTableID。PK 列上有一個唯一的聚集索引,MyTableID名為PK_MyTable.

我注意到該表上有一個額外的非聚集唯一索引IX_MyTable_MytableID,只有一個 column MyTableID,沒有其他包含的列。這個索引顯然是多餘的,但是當我嘗試刪除它時,我收到一條錯誤消息:

The constraint 'IX_MyTable_MyTableID' is being referenced by table 'OtherTable', 
foreign key constraint 'FK__OtherTable__MyTableID__369C23FC'.

為什麼FK約束依賴非聚集唯一索引而不是主鍵約束?如何更新 FK 以使用聚群 PK 索引而不是其他索引?

因為外鍵可以指向主鍵唯一約束,並且創建該外鍵的人可能在主鍵存在之前創建了它(或者他們在更改有關主鍵的其他內容時將 FK 轉換為指向唯一索引)。這很容易重現:

CREATE TABLE dbo.MyTable(MyTableID INT NOT NULL, CONSTRAINT myx UNIQUE(MyTableID));

CREATE TABLE dbo.OtherTable1(ID INT FOREIGN KEY REFERENCES dbo.MyTable(MyTableID));

ALTER TABLE dbo.MyTable ADD CONSTRAINT PKmyx PRIMARY KEY(MyTableID);

CREATE TABLE dbo.OtherTable2(ID INT FOREIGN KEY REFERENCES dbo.MyTable(MyTableID));

事實上,這兩個外鍵都將指向該列上定義的第一個myx唯一約束 ( )。

您可以通過刪除並重新創建它來修復另一個表上的外鍵。您將需要對指向該列的任何其他表重複該過程。你可以很容易地找到這些:

SELECT s.name,t.name,fk.name
FROM sys.foreign_key_columns AS fkc
INNER JOIN sys.foreign_keys AS fk
ON fkc.constraint_object_id = fk.[object_id]
INNER JOIN sys.tables AS t
ON fkc.parent_object_id = t.[object_id]
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
INNER JOIN sys.columns AS c1
ON c1.[object_id] = fkc.referenced_object_id
AND c1.column_id = fkc.referenced_column_id
AND c1.name = N'MyTableID'
WHERE fkc.referenced_object_id = OBJECT_ID('dbo.MyTable');

結果:

dbo    OtherTable1    FK__OtherTable1__ID__32E0915F
dbo    OtherTable2    FK__OtherTable2__ID__35BCFE0A

甚至生成一個腳本來刪除和重新創建它們(同時刪除冗餘的唯一約束):

DECLARE 
 @sql1 NVARCHAR(MAX) = N'', 
 @sql2 NVARCHAR(MAX) = N'ALTER TABLE dbo.MyTable DROP CONSTRAINT myx;', 
 @sql3 NVARCHAR(MAX) = N'';

SELECT 
 @sql1 += N'
ALTER TABLE ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
 + ' DROP CONSTRAINT ' + QUOTENAME(fk.name) + ';',
 @sql3 += N'
ALTER TABLE ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name)
 + ' ADD CONSTRAINT ' + QUOTENAME(fk.name) + ' FOREIGN KEY '
 + '(' + QUOTENAME(c2.name) + ') REFERENCES dbo.MyTable(MyTableID);'
FROM sys.foreign_key_columns AS fkc
INNER JOIN sys.foreign_keys AS fk
ON fkc.constraint_object_id = fk.[object_id]
INNER JOIN sys.tables AS t
ON fkc.parent_object_id = t.[object_id]
INNER JOIN sys.schemas AS s
ON t.[schema_id] = s.[schema_id]
INNER JOIN sys.columns AS c1
ON c1.[object_id] = fkc.referenced_object_id
AND c1.column_id = fkc.referenced_column_id
AND c1.name = N'MyTableID'
INNER JOIN sys.columns AS c2
ON c2.[object_id] = fkc.parent_object_id
AND c2.column_id = fkc.parent_column_id
WHERE fkc.referenced_object_id = OBJECT_ID('dbo.MyTable');

PRINT @sql1;
PRINT @sql2;
PRINT @sql3;
-- EXEC sp_executesql @sql1;
-- EXEC sp_executesql @sql2;
-- EXEC sp_executesql @sql3;

結果:

ALTER TABLE [dbo].[OtherTable1] DROP CONSTRAINT [FK__OtherTable1__ID__32E0915F];
ALTER TABLE [dbo].[OtherTable2] DROP CONSTRAINT [FK__OtherTable2__ID__35BCFE0A];

ALTER TABLE dbo.MyTable DROP CONSTRAINT myx;

ALTER TABLE [dbo].[OtherTable1] ADD CONSTRAINT [FK__OtherTable1__ID__32E0915F] 
 FOREIGN KEY ([ID]) REFERENCES dbo.MyTable(MyTableID);
ALTER TABLE [dbo].[OtherTable2] ADD CONSTRAINT [FK__OtherTable2__ID__35BCFE0A] 
 FOREIGN KEY ([ID]) REFERENCES dbo.MyTable(MyTableID);

這顯式地處理了這種情況,其中約束只涉及單個列。如果涉及多個列,它會變得有點複雜(這個答案並不是為了解決這個問題)。如果外鍵指向冗餘唯一索引(具有相同的底層結構但使用稍微不同的 DDL 創建),我也沒有測試這是否與編碼完全相同。為讀者練習。:-)

引用自:https://dba.stackexchange.com/questions/48634