【问题标题】:Change a Primary Key from Nonclustered to Clustered将主键从非集群更改为集群
【发布时间】:2011-01-18 20:01:58
【问题描述】:

假设我有一个 SQL Server 2005 表 TableX,上面有 2 个索引:

PK_TableX = 字段 A 上的主键非群集
IX_TableX_FieldB = 在 FieldB 上集群

我想将 PK 切换为 CLUSTERED,并将另一个索引切换为 NONCLUSTERED。

我必须假设数据库将在我尝试更改索引时正在使用中 - 所以我想避免的主要问题是,在过程中的某个时刻,PK 约束将不存在桌子。我希望免受插入重复密钥的风险。

即我不能只删除主键并重新创建它。

此过程需要通过 SQL 脚本完成,而不是通过 SSMS。

我有一种我认为可行的方法(我会将其发布为可能的答案),但我想打开它以防我遗漏某些东西或有其他/更好的方法。另外,它可能在未来对其他人有用

【问题讨论】:

  • 为什么要集群在主键上?它是 GUID 还是 INT?您的大多数查询是否包括匹配 PK?
  • @awright18 - 集群 PK 提供更好的性能,它实际上是最常查询的 2 个 int 字段上的复合主键

标签: sql sql-server sql-server-2005 indexing


【解决方案1】:

1)先删除已有的聚集索引(IX_TableX_FieldB):

   DROP INDEX TableX.IX_TableX_FieldB

2) 对主键中引用的唯一字段创建(临时)UNIQUE 约束

    ALTER TABLE TableX
    ADD CONSTRAINT UQ_TableX UNIQUE(FieldA)

3) 删除主键

    ALTER TABLE TableX
    DROP CONSTRAINT PK_TableX

4) 将 PRIMARY KEY 重新创建为 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)

【讨论】:

  • 这是我最终采用的方法,似乎是最好的方法。
  • 这行得通。为什么当我尝试使用 UI 执行此操作时,它在第一步失败并重新添加聚集索引?
  • 如果您有依赖的外键或约束,请按照此dba.stackexchange.com/questions/48634/…
  • 不要忘记删除聚集索引会在“堆”中重建整个表,如果表很大,这可能需要很长时间。
【解决方案2】:

我知道这是旧的,但这将编写出所有 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 '+@table+' 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 '+@PKTableName+' DROP CONSTRAINT '+ @PKName
    PRINT @sql

END
SET @sql = 'ALTER TABLE '+@PKTableName +' ADD  CONSTRAINT '+@PKName+' PRIMARY KEY CLUSTERED ('+@PKcolumnName+' 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 '+@table+' WITH NOCHECK ADD  CONSTRAINT  '+ @FKName+' FOREIGN KEY(['+@FKColumnName+'])
        REFERENCES ['+@PKTableName+'] (['+@PKcolumnName+'])'
        PRINT(@sql)
        SET @sql = 'ALTER TABLE '+@table+' CHECK CONSTRAINT  '+@FKName
        PRINT(@sql)

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

         END
CLOSE table_cur2
DEALLOCATE table_cur2
DROP TABLE FKAgainstTableList

【讨论】:

  • “set @PKcolumnName”不正确,它检查标识列而不是主键列。如果主键列是标识列,它当然可以工作。
  • 如果您确定只有一个主键列,请将set @PKColumnName 更改为SELECT @PKcolumnName=column_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 AND table_name = @PKTableName
  • 另请注意,PK 索引的 FILLFACTOR=80 通常不是最佳的。当然,这一切都取决于表。就我而言,我省略了 FILLFACTOR 以使用默认值 (100%)。
  • 为了让它在 2019 年发挥作用,我不得不用双引号将 @FKName 括起来
【解决方案3】:

这是覆盖原始键的快捷方式:

CREATE UNIQUE CLUSTERED INDEX [pk_name] ON [table_name]([id])
WITH DROP_EXISTING

正如其他答案中提到的,如果存在,您仍然需要删除/重新添加外部约束。

【讨论】:

    【解决方案4】:

    在 Jerloldrartolf 的答案中添加双引号

    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 '+@table+' 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 '+@PKTableName+' DROP CONSTRAINT "'+ @PKName +'"'
        PRINT @sql
    
    END
    SET @sql = 'ALTER TABLE '+@PKTableName +' ADD  CONSTRAINT '+@PKName+' PRIMARY KEY CLUSTERED ('+@PKcolumnName+' 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 '+@table+' WITH NOCHECK ADD  CONSTRAINT  "'+ @FKName+'" FOREIGN KEY(['+@FKColumnName+'])
            REFERENCES ['+@PKTableName+'] (['+@PKcolumnName+'])'
            PRINT(@sql)
            SET @sql = 'ALTER TABLE '+@table+' CHECK CONSTRAINT  "'+@FKName+'"'
            PRINT(@sql)
    
            FETCH NEXT FROM table_cur2 INTO @table,@FKColumnName,@FKName
    
             END
    CLOSE table_cur2
    DEALLOCATE table_cur2
    DROP TABLE FKAgainstTableList
    

    我发现我需要分三部分运行生成的脚本

    从这部分生成是中间部分。

    SET @sql = 'ALTER TABLE '+@PKTableName +' ADD  CONSTRAINT '+@PKName+' PRIMARY KEY CLUSTERED ('+@PKcolumnName+' 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)
    

    【讨论】:

      猜你喜欢
      • 2011-02-05
      • 2011-01-09
      • 2012-05-29
      • 1970-01-01
      • 1970-01-01
      • 2011-11-28
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      相关资源
      最近更新 更多