2010-02-19 19 views
27

Supponiamo che io sono uno SQL Server 2005 tabella, TableX, con 2 indici su di esso:
Modificare una chiave primaria da non cluster a cluster

PK_TableX = PRIMARY KEY non cluster FieldA
IX_TableX_FieldB = cluster FieldB

Desidero cambiare il PK in CLUSTERED e l'altro indice in NONCLUSTERED.

Devo supporre che il database sarà in uso nel momento in cui provo a cambiare gli indici, quindi la mia preoccupazione principale che voglio evitare è che in un certo punto del processo il vincolo PK non esisterà sul tavolo. Voglio essere protetto contro qualsiasi rischio di inserimento di chiavi duplicate.

Ad esempio, non posso semplicemente rilasciare la chiave primaria e ricrearla.

Questo processo deve essere eseguito tramite uno script SQL, non tramite SSMS.

Ho un approccio che penso funzionerà (lo posterò come una potenziale risposta), ma vorrei aprirlo nel caso mi manchi qualcosa o c'è un altro/modo migliore. Inoltre, può risultare utile per gli altri in futuro

+0

Perché si desidera eseguire il cluster sulla chiave primaria? È un GUID o INT? La maggior parte dei tuoi querys include la corrispondenza con il PK? – awright18

+0

@ awright18 - il PK in cluster offre prestazioni migliori, in realtà è una chiave primaria composta su 2 campi int che più spesso interrogati su – AdaTheDev

risposta

39

1) Eliminare l'indice esistente cluster prima (IX_TableX_FieldB):

DROP INDEX TableX.IX_TableX_FieldB 

2) Creare una (temporanea) vincolo UNIQUE sui campi unici a cui fa riferimento la chiave primaria

ALTER TABLE TableX 
    ADD CONSTRAINT UQ_TableX UNIQUE(FieldA) 

3) Eliminare la PRIMARY KEY

ALTER TABLE TableX 
    DROP CONSTRAINT PK_TableX 

4) Ricreare la chiave primaria come RAGRUPPATO

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

5) Eliminare il vincolo UNIQUE temporanea

ALTER TABLE TableX 
    DROP CONSTRAINT UQ_TableX 

6) Aggiungere l'IX_TableX_FieldB indietro come NONCLUSTERED

CREATE NONCLUSTERED INDEX IX_TableX_FieldB ON TableX(FieldB) 
+1

Questo è l'approccio che ho seguito alla fine, sembra il modo migliore. – AdaTheDev

+0

Questo funziona. Perché quando provo a farlo usando l'interfaccia utente, fallisce il primo passaggio e riaggiunge l'indice cluster? – MetalPhoenix

+0

Se si dispone di chiavi o vincoli esterni dipendenti, seguire questo http://dba.stackexchange.com/questions/48634/unable-to-drop-non-pk-index-because-it-is-referenced-in-a- vincolo di chiave esterna – qub1n

9

So che questo è vecchio ma questo scriverà tutte le drop dell'FK, il pk drop, il pk ricreerà, l'FK ricrea. Sostituisci MYTABLE con il nome della tabella.

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

Theet @ PKcolumnName' non è corretto, verifica la presenza di una colonna Identity e non la colonna della chiave primaria.Funzionerà ovviamente se la colonna della chiave primaria è la colonna Identity. –

+0

Se si è sicuri che esiste una sola colonna di chiave primaria, modificare 'set @ PKColumnName' da' SELECT @ PKcolumnName = nome_colonna FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE OBJECTPROPERTY (OBJECT_ID (constraint_name), 'IsPrimaryKey') = 1 AND table_name = @PKTableName ' –

+0

Si noti inoltre che un FILLFACTOR = 80 per l'indice PK spesso non è ottimale. Tutto dipende ovviamente dalla tabella. Nel mio caso ho omesso il FILLFACTOR di avere il valore predefinito (100%). –

Problemi correlati