2010-02-19 88 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