Sql-Server
無法刪除非 PK 索引,因為它在外鍵約束中被引用
我有一張名為
MyTable
. 主鍵是一個名為 的標識 int 列MyTableID
。PK 列上有一個唯一的聚集索引,MyTableID
名為PK_MyTable
.我注意到該表上有一個額外的非聚集唯一索引
IX_MyTable_MytableID
,只有一個 columnMyTableID
,沒有其他包含的列。這個索引顯然是多餘的,但是當我嘗試刪除它時,我收到一條錯誤消息: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 創建),我也沒有測試這是否與編碼完全相同。為讀者練習。:-)