2010-02-19 16 views
27

Supongamos que tengo un servidor SQL mesa de 2005, TABLEX, con 2 índices en él:
cambiar una clave principal de no agrupado a Agrupado

PK_TableX = PRIMARY KEY no agrupados en FieldA
IX_TableX_FieldB = agrupado en FieldB

Quiero cambiar el PK que se CLUSTERARÁ, y el otro índice no estará CLASIFICADO.

Tengo que asumir que la base de datos estará en uso en el momento en que intento cambiar la ronda de índices, por lo que mi principal preocupación es evitar que la restricción PK no exista en algún momento del proceso. en la mesa. Quiero estar protegido contra cualquier riesgo de que se inserten claves duplicadas.

es decir, no puedo simplemente soltar la clave principal y volver a crearla.

Este proceso debe realizarse mediante un script SQL, no a través de SSMS.

Tengo un enfoque que creo que funcionará (lo publicaré como posible respuesta), pero me gustaría abrirlo en caso de que me falta algo o hay otra/mejor manera. Además, puede resultar útil para otros en el futuro

+0

¿Por qué desea agrupar en clúster en la clave principal? ¿Es un GUID o INT? ¿La mayoría de tus consultas incluyen hacer coincidir el PK? – awright18

+0

@ awright18 - PK agrupado ofrece un mejor rendimiento, en realidad es una clave primaria compuesta en 2 campos int que con más frecuencia se consulta en – AdaTheDev

Respuesta

39

1) Quitar el índice existente en clúster primero (IX_TableX_FieldB):

DROP INDEX TableX.IX_TableX_FieldB 

2) Crear un() restricción UNIQUE temporal en los campos únicos que se hace referencia en el clave primaria

ALTER TABLE TableX 
    ADD CONSTRAINT UQ_TableX UNIQUE(FieldA) 

3) la caída de la PRIMARY KEY

ALTER TABLE TableX 
    DROP CONSTRAINT PK_TableX 

4) Volver a crear la clave principal como CLUSTERED

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

5) La caída de la restricción UNIQUE temporal

ALTER TABLE TableX 
    DROP CONSTRAINT UQ_TableX 

6) Añadir el IX_TableX_FieldB de nuevo tan NONCLUSTERED

CREATE NONCLUSTERED INDEX IX_TableX_FieldB ON TableX(FieldB) 
+1

Este es el enfoque con el que fui finalmente, parece la mejor manera. – AdaTheDev

+0

Esto funciona. ¿Por qué es que cuando trato de hacer esto usando la IU, falla en el primer paso y vuelvo a agregar el índice agrupado? – MetalPhoenix

+0

Si tiene claves foráneas dependientes o restricciones, siga esto http://dba.stackexchange.com/questions/48634/unable-to-drop-non-pk-index-because-it-is-referenced-in-a- foreign-key-constraint – qub1n

9

Sé que esto es viejo, pero Esto procesará todas las gotas de FK, la caída de pk, la recreación de pk, la recreación de FK. Reemplace MYTABLE con el nombre de su tabla.

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

Theetc @ PKcolumnName' no es correcto, busca una columna de identidad y no la columna de clave principal.Funcionará, por supuesto, si la columna de la clave principal es la columna de identidad. –

+0

Si está seguro de que solo hay una columna de clave principal, cambie 'set @ PKColumnName' por' SELECT @ PKcolumnName = column_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE OBJECTPROPERTY (OBJECT_ID (constraint_name), 'IsPrimaryKey') = 1 AND table_name = @PKTableName ' –

+0

También tenga en cuenta que un FILLFACTOR = 80 para el índice PK a menudo no es óptimo. Todo depende de la mesa, por supuesto. En mi caso, he omitido el FILLFACTOR para tener el valor predeterminado (100%). –

Cuestiones relacionadas