2010-02-19 83 views
27

假設我有一個SQL Server 2005的表,TableX的,與2個指標就可以了:
從非聚簇更改主鍵聚集

PK_TableX = PRIMARY KEY NONCLUSTERED上FIELDA
IX_TableX_FieldB =聚集在FieldB

我想將PK切換爲CLUSTERED,而另一個索引是NONCLUSTERED。

我不得不假設數據庫將在我嘗試更改索引輪的時刻使用 - 所以我主要關心的是我想要避免的是在該進程中的某個點PK約束不會存在在桌子上。我想要防止重複鍵被插入的風險。

即我不能放棄主鍵並重新創建它。

此過程需要通過SQL腳本完成,而不是通過SSMS。

我有一個我認爲可行的方法(我會將它作爲潛在答案發布),但是想要打開它以防萬一我失去了某些東西或者有其他更好的方法。另外,它可以證明是有用的人,將來

+0

爲什麼要在主鍵上進行集羣?它是一個GUID還是INT?大部分查詢是否包含匹配PK? – awright18 2010-02-20 01:23:44

+0

@ awright18 - 聚集的PK提供了更好的性能,它實際上是在最常被查詢的2個int字段上的複合主鍵 – AdaTheDev 2010-02-20 11:08:16

回答

39

1)刪除現有的聚集索引第一(IX_TableX_FieldB):

DROP INDEX TableX.IX_TableX_FieldB 

2)創建上所引用的唯一字段(臨時)唯一約束主鍵

ALTER TABLE TableX 
    ADD CONSTRAINT UQ_TableX UNIQUE(FieldA) 

3)刪除該PRIMARY KEY

ALTER TABLE TableX 
    DROP CONSTRAINT PK_TableX 

4 )重新將主鍵CLUSTERED

ALTER TABLE TableX 
    ADD CONSTRAINT PK_TableX PRIMARY KEY CLUSTERED(FieldA) 

5)刪除臨時UNIQUE約束

ALTER TABLE TableX 
    DROP CONSTRAINT UQ_TableX 

6)添加IX_TableX_FieldB回爲NONCLUSTERED

CREATE NONCLUSTERED INDEX IX_TableX_FieldB ON TableX(FieldB) 
+1

這是我最終使用的方法,似乎是最好的方法。 – AdaTheDev 2010-02-22 08:21:35

+0

這有效。爲什麼當我嘗試使用UI執行此操作時,它在第一步中失敗並重新添加聚簇索引? – MetalPhoenix 2015-05-20 18:30:38

+0

如果您有從屬外鍵或約束,請按照http://dba.stackexchange.com/questions/48634/unable-to-drop-non-pk-index-because-it-is-referenced-in-a-外鍵約束 – qub1n 2015-10-05 20:48:02

9

我知道這是舊的,但這將編寫出所有FK掉落,PK掉落,pk重新創建,FK重新創建。 用你的表名替換MYTABLE。

IF EXISTS (SELECT * FROM sys.tables WHERE object_id = OBJECT_ID(N'[dbo].[FKAgainstTableList]')) 
BEGIN 
    DROP TABLE FKAgainstTableList 
END 
--CREATE TABLE FKAgainstTableList (ForeignKey VARCHAR(30),[Table] VARCHAR(30)) 
DECLARE @PKTableName VARCHAR(100), 
     @PKName varchar(100), 
     @FKName varchar(100), 
     @sql varchar(max), 
     @PKcolumnName varchar(30), 
     @table VARCHAR(100), 
     @FKColumnName VARCHAR(100), 
     @parentColumnNumber int 
SET @PKTableName = 'MYTABLE' 
set @PKName = (SELECT name FROM sys.indexes WHERE OBJECT_NAME(object_id) = @PKTableName AND is_primary_key = 1) 
set @PKcolumnName = (SELECT name FROM sys.columns WHERE OBJECT_NAME(object_id) = @PKTableName AND is_identity =1) 
PRINT @PKcolumnName 

SELECT OBJECT_NAME(sys.foreign_key_columns.parent_object_id) [Table],sys.columns.name [FKColumnName],sys.foreign_keys.name [FKName] 
    INTO FKAgainstTableList 
    FROM sys.foreign_keys INNER JOIN sys.foreign_key_columns 
    ON sys.foreign_keys.object_id = sys.foreign_key_columns.constraint_object_id 
    INNER JOIN sys.columns ON sys.columns.object_id = sys.foreign_keys.parent_object_id AND sys.columns.column_id = sys.foreign_key_columns.parent_column_id 
    WHERE OBJECT_NAME(sys.foreign_keys.referenced_object_id) = @PKTableName 


DECLARE table_cur1 CURSOR FOR 
    SELECT * FROM FKAgainstTableList 

    PRINT @sql 

-------------------------------Disable constraint on FK Tables 
OPEN table_cur1 
FETCH NEXT FROM table_cur1 INTO @table,@FKColumnName,@FKName 
WHILE @@FETCH_STATUS = 0 
    BEGIN 
     SET @sql ='ALTER TABLE '[email protected]+' DROP CONSTRAINT '+ @FKName 
     PRINT @sql 
     FETCH NEXT FROM table_cur1 INTO @table,@FKColumnName,@FKName 
    END 
CLOSE table_cur1 
DEALLOCATE table_cur1 
--------------------------------DROP AND recreate CLUSTERED pk 
IF EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(@PKTableName) AND name = @PKName) 
BEGIN 
    SET @sql = 'ALTER TABLE '[email protected]+' DROP CONSTRAINT '+ @PKName 
    PRINT @sql 

END 
SET @sql = 'ALTER TABLE '[email protected] +' ADD CONSTRAINT '[email protected]+' PRIMARY KEY CLUSTERED ('[email protected]+' ASC) 
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]' 
PRINT(@sql) 

--------------------------------Enable FK constraints on FK tables. 
DECLARE table_cur2 CURSOR FOR 
    SELECT * FROM FKAgainstTableList 
OPEN table_cur2 
FETCH NEXT FROM table_cur2 INTO @table,@FKColumnName,@FKName 
WHILE @@FETCH_STATUS = 0 
    BEGIN 
     SET @sql = 'ALTER TABLE '[email protected]+' WITH NOCHECK ADD CONSTRAINT '+ @FKName+' FOREIGN KEY(['[email protected]+']) 
     REFERENCES ['[email protected]+'] (['[email protected]+'])' 
     PRINT(@sql) 
     SET @sql = 'ALTER TABLE '[email protected]+' CHECK CONSTRAINT '[email protected] 
     PRINT(@sql) 

     FETCH NEXT FROM table_cur2 INTO @table,@FKColumnName,@FKName 

     END 
CLOSE table_cur2 
DEALLOCATE table_cur2 
DROP TABLE FKAgainstTableList 
+0

'set @ PKcolumnName'不正確,它檢查標識列而不是主鍵列。如果主鍵列是標識列,它當然會起作用。 – 2014-02-13 09:53:39

+0

如果您確定只有一個主鍵列,請通過SELECT @ PKcolumnName = column_name更改'set @ PKColumnName' FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name),'IsPrimaryKey')= 1 AND table_name = @PKTableName ' – 2014-02-13 10:04:44

+0

還要注意PK指數的FILLFACTOR = 80通常不是最優的。這一切都取決於表格。在我的情況下,我省略了FILLFACTOR以擁有默認值(100%)。 – 2014-02-13 10:17:19