2009-08-11 13 views
14

Ho una colonna smalldatetime che devo modificare per essere una colonna datetime. Questo è qualcosa che farà parte di un processo di installazione, quindi non può essere una procedura manuale. Sfortunatamente, la colonna ha alcuni indici e un vincolo non nullo su di essa. Gli indici sono correlati alle prestazioni e dovrebbero essere mantenuti solo utilizzando il nuovo tipo di dati. È possibile scrivere una dichiarazione che mi consenta di conservare le informazioni pertinenti mentre altero ancora il tipo di dati della colonna? Se è così, come può essere fatto?Mantieni gli indici SQL durante la modifica del tipo di dati colonna

risposta

10

Non è possibile modificare il tipo di dati da smalldatetime in datetime con gli indici, vincoli univoci, vincoli di chiave esterna o controllare i vincoli al suo posto. Dovrai lasciarli tutti prima di cambiare il tipo. Quindi:

alter table T alter column TestDate datetime not null 

Quindi ricreare i vincoli e gli indici che si applicano ancora.


Alcuni approcci diversi per la generazione della goccia e crea:

1) Se avete dato nomi espliciti per tutti gli indici ei vincoli allora il vostro installatore può eseguire uno script statica in ogni ambiente (dev, prova, . utente test di accettazione, test delle prestazioni, ecc, produzione)

per generare questo script esplicito è possibile: a) Utilizzare SQL Server Management Studio (o con SQL Server 2000, enterprise Manager) per lo script le dichiarazioni creare e rilasciare. b) Lavora dal tuo repository del codice sorgente per scoprire i nomi e le definizioni degli oggetti dipendenti e mettere insieme lo script statico appropriato. c) Tentativo di eseguire l'istruzione alter. Guarda cosa fallisce. Cerca le definizioni e scrivi la goccia e crea. (Personalmente, questo sarebbe fantastico per scrivere il drop, non così bravo nel creare.)

2) Se non sono stati forniti nomi espliciti per tutti gli indici e i vincoli, il programma di installazione dovrà interrogare il dizionario dei dati per i nomi appropriati e utilizzare SQL dinamico per eseguire le gocce, nell'ordine corretto, prima di l'istruzione alter column e quindi crea, nell'ordine corretto, dopo la colonna alter.

Questo sarà più semplice se si sa che non ci sono vincoli e solo indici.

Ci possono essere strumenti o librerie che già sanno come fare questo.

Inoltre, se si tratta di un'applicazione pacchettizzata, potrebbe non essere sicuro che gli amministratori di database locali non abbiano aggiunto indici.

NOTA: se esiste un vincolo univoco, avrà creato un indice, che non sarà possibile eliminare con DROP INDEX.

+0

Sono stato in grado di trovare la fonte sql dietro la creazione degli indici ed è stato in grado di utilizzare quelli per eliminarli prima di modificare il tipo. Per quanto riguarda il vincolo, ho trovato una query tramite google che può essere utilizzata per determinare il nome del vincolo generato casualmente. –

+0

dichiarare @constraintName come nvarchar (100) dichiarare nvarchar @sql (1000) \t selezionare @constraintName = O.name \t da sysobjects AS O \t left join sysobjects AS T \t \t su O.parent_obj = T. id \t dove isnull (objectproperty (O.id, 'IsMSShipped'), 1) = 0 \t \t e O.Nome non come '% dtproper%' \t \t e O.name non come 'dt [_]%' \t \t e T.name = 'MyTable' \t \t e O.name come 'DF__MyTabl__MyCol%' \t se non è nulla @constraintName \t iniziano \t \t selezionare @sql = 'ALTER TABLE [MyTable] CADUTA CONSTRAINT [' + @constraintName + ']' \t \t eseguire sp_executesql @sql \t fine –

4

MODIFICA: Dipende dal tipo di dati originale e modificato. Se si tenta di modificare una colonna da varchar a nvarchar, fallirà. Considerando che, se si modifica la colonna da varchar (16) a varchar (32), avrà successo.

--Disable Index 
ALTER INDEX MyIndex ON MyTable DISABLE 
GO 

-- Change column datatype 

--Enable Index 
ALTER INDEX MyIndex ON MyTable REBUILD 
GO 

Se si modifica il tipo di una colonna, sarà necessario ricostruire tutti gli indici che utilizzano tale colonna.

Ma a meno che non si disponga di enormi volumi di dati (o eseguiti 24 ore su 24, 7 giorni su 7), la ricostruzione degli indici non è un grosso problema. Basta programmare una finestra di manutenzione.

+0

Il che andrebbe bene, suppongo, per Phillip, a patto che rimangano incollati e ricostruiti automaticamente usando il nuovo tipo di dati. – Thilo

+0

Quando provo e faccio funzionare ALTER INDEX [MyIndex] su [MyTable] disabilita ottengo l'errore "Sintassi non corretta in prossimità della parola chiave 'INDEX'. Mi sto perdendo qualcosa? –

+0

Dal distacco questo commento ho trovato che SQL Server 2000 non consente di disabilitare un indice, deve essere eliminato e creato. Abbiamo alcuni server che sono ancora su SQL Server 2000, quindi il comando disable non funzionerà per me. –

6

Se si modifica solo la dimensione, l'indice rimarrà sul tavolo.

Se si modifica il tipo di dati, verrà visualizzato un messaggio di errore che indica che gli oggetti dipendono dalla colonna che si sta tentando di modificare e pertanto non sarà possibile modificarlo.

È possibile eseguire lo script degli indici in questione manualmente o tramite script. In SSMS, fare clic con il pulsante destro del mouse sulla tabella e creare lo script per l'oggetto in questione.

Se si desidera lo script di indice programmatico, ecco un processo memorizzato che ho utilizzato che ho ricevuto da un mio ex collega.

Drop Proc ScriptIndex 
GO 
Create Proc ScriptIndex 
    @TableName  VarChar (Max), 
    @IndexScript VarChar (Max) OUTPUT 
AS 

-- Get all existing indexes, EXCEPT the primary keys 
DECLARE cIX CURSOR FOR 
SELECT OBJECT_NAME(SI.Object_ID), SI.Object_ID, SI.Name, SI.Index_ID 
FROM Sys.Indexes SI 
    LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC 
     ON SI.Name = TC.CONSTRAINT_NAME 
     AND OBJECT_NAME(SI.Object_ID) = TC.TABLE_NAME 
WHERE 1=1 
    AND OBJECT_NAME(SI.Object_ID) = @TableName 
    AND TC.CONSTRAINT_NAME IS NULL 
    AND OBJECTPROPERTY(SI.Object_ID, 'IsUserTable') = 1 
ORDER BY OBJECT_NAME(SI.Object_ID), SI.Index_ID 

DECLARE @IxTable SYSNAME 
DECLARE @IxTableID INT 
DECLARE @IxName SYSNAME 
DECLARE @IxID INT 

-- Loop through all indexes 
OPEN cIX 
FETCH NEXT FROM cIX INTO @IxTable, @IxTableID, @IxName, @IxID 
WHILE (@@FETCH_STATUS = 0) 
BEGIN 
    DECLARE @IXSQL NVARCHAR(4000) 
    DECLARE @PKSQL NVARCHAR(4000) 
    SET @PKSQL = '' 
    SET @IXSQL = 'CREATE ' 

    -- Check if the index is unique 
    IF (INDEXPROPERTY(@IxTableID, @IxName, 'IsUnique') = 1) 
     SET @IXSQL = @IXSQL + 'UNIQUE ' 
    -- Check if the index is clustered 
    IF (INDEXPROPERTY(@IxTableID, @IxName, 'IsClustered') = 1) 
     SET @IXSQL = @IXSQL + 'CLUSTERED ' 

    SET @IXSQL = @IXSQL + 'INDEX ' + @IxName + ' ON ' + @IxTable + '(' 

    -- Get all columns of the index 
    DECLARE cIxColumn CURSOR FOR 
     SELECT SC.Name 
     FROM Sys.Index_Columns IC 
     JOIN Sys.Columns SC ON IC.Object_ID = SC.Object_ID AND IC.Column_ID = SC.Column_ID 
     WHERE IC.Object_ID = @IxTableID AND Index_ID = @IxID 
     ORDER BY IC.Index_Column_ID 

    DECLARE @IxColumn SYSNAME 
    DECLARE @IxFirstColumn BIT SET @IxFirstColumn = 1 

    -- Loop throug all columns of the index and append them to the CREATE statement 
    OPEN cIxColumn 
    FETCH NEXT FROM cIxColumn INTO @IxColumn 
    WHILE (@@FETCH_STATUS = 0) 
    BEGIN 
     IF (@IxFirstColumn = 1) 
     SET @IxFirstColumn = 0 
     ELSE 
     SET @IXSQL = @IXSQL + ', ' 

     SET @IXSQL = @IXSQL + @IxColumn 

     FETCH NEXT FROM cIxColumn INTO @IxColumn 
    END 
    CLOSE cIxColumn 
    DEALLOCATE cIxColumn 

    SET @IXSQL = @IXSQL + ')' 
    -- Print out the CREATE statement for the index 
    PRINT @IXSQL 

    FETCH NEXT FROM cIX INTO @IxTable, @IxTableID, @IxName, @IxID 
END 

CLOSE cIX 
DEALLOCATE cIX 

GO 
Declare @TableName VarChar (Max), @IndexScript VarChar (Max) 

Exec ScriptIndex 'Client', @IndexScript OUTPUT 
Print @IndexScript 
+0

Questo è molto utile SP, ma non fa distinzione tra le colonne nell'indice e le colonne incluse e in questo modo minaccia tutte le colonne come stesse. –

0

La cosa migliore da fare è creare una procedura che restituisca lo script indice di una determinata tabella/colonna. Quindi è possibile rimuovere gli indici solo dalla colonna che viene modificata e non tutti gli indici dalla tabella, mentre la creazione di indici può essere piuttosto costosa.

  1. Memorizza il risultato della procedura in un DataTable
  2. Elimina gli indici della colonna
  3. Modifica la colonna
  4. ricostruire gli indici memorizzati nella datatable

    -- objective : Generates indices scripting using specified column 
    -- Parameters : 
    --  @Tabela -> Name of the table that the column belongs to 
    --  @Coluna -> Name of the column that will be searched for the indices to generate the script 
    --Use: proc_ScriptIndexColumn 'TableName', 'CollumnName' 
    
    SET ANSI_NULLS ON 
    GO 
    SET QUOTED_IDENTIFIER ON 
    GO 
    Create Proc proc_ScriptIndexColumn (@Tabela VARCHAR(4000), @Coluna VARCHAR(4000)) 
    AS 
    BEGIN  
        DECLARE @isql_key VARCHAR(4000), 
         @isql_incl VARCHAR(4000), 
         @tableid INT, 
         @indexid INT   
    DECLARE @tablename VARCHAR(4000), 
         @indexname VARCHAR(4000)   
    DECLARE @isunique INT, 
         @isclustered INT, 
         @indexfillfactor INT   
    DECLARE @srsql VARCHAR(MAX)   
    DECLARE @ScriptsRetorno TABLE 
         (Script VARCHAR(MAX))   
    DECLARE index_cursor CURSOR 
        FOR 
        SELECT tablename = OBJECT_NAME(i.[object_id]), 
          tableid  = i.[object_id], 
          indexid  = i.index_id, 
          indexname  = i.name, 
          isunique  = i.is_unique, 
          CASE I.type_desc 
           WHEN 'CLUSTERED' THEN 1 
           ELSE 0 
          END      AS isclustered, 
          indexfillfactor = i.fill_factor     
        FROM sys.indexes    AS i 
          INNER JOIN SYSOBJECTS AS O 
           ON I.[object_id] = O.ID 
          INNER JOIN sys.index_columns AS ic 
           ON (ic.column_id > 0 
             AND (ic.key_ordinal > 0 
               OR ic.partition_ordinal = 0 
               OR ic.is_included_column != 0 
              )) 
           AND ( ic.index_id = CAST(i.index_id AS INT) 
             AND ic.object_id = i.[object_id] 
            ) 
          INNER JOIN sys.columns AS sc 
            ON sc.object_id = ic.object_id 
           AND sc.column_id = ic.column_id 
        WHERE O.XTYPE = 'U' 
          AND i.typE = 2 /*Non clustered*/ 
          AND i.is_unique = 0 
          AND i.is_hypothetical = 0 
          AND UPPER(OBJECT_NAME(i.[object_id])) = UPPER(@Tabela) 
          AND UPPER(sc.name) = UPPER(@Coluna)  
    
    OPEN index_cursor 
    FETCH NEXT FROM index_cursor INTO @tablename,@tableid, @indexid,@indexname , 
    @isunique ,@isclustered , @indexfillfactor  
    WHILE @@fetch_status <> -1 
    BEGIN 
        SELECT @isql_key = '', 
          @isql_incl = ''   
        SELECT @isql_key = CASE ic.is_included_column 
              WHEN 0 THEN CASE ic.is_descending_key 
                   WHEN 1 THEN @isql_key +COALESCE(sc.name, '') + 
                    ' DESC, ' 
                   ELSE @isql_key + COALESCE(sc.name, '') 
                    + ' ASC, ' 
                 END 
              ELSE @isql_key 
             END, 
          --include column 
          @isql_incl = CASE ic.is_included_column 
               WHEN 1 THEN CASE ic.is_descending_key 
                   WHEN 1 THEN @isql_incl + 
                    COALESCE(sc.name, '') + 
                    ', ' 
                   ELSE @isql_incl + COALESCE(sc.name, '') 
                    + ', ' 
                  END 
               ELSE @isql_incl 
             END 
        FROM sysindexes i 
          INNER JOIN sys.index_columns AS ic 
           ON ( 
             ic.column_id > 0 
             AND ( 
               ic.key_ordinal > 0 
               OR ic.partition_ordinal = 0 
               OR ic.is_included_column != 0 
              ) 
            ) 
           AND (ic.index_id = CAST(i.indid AS INT) AND ic.object_id = i.id) 
          INNER JOIN sys.columns AS sc 
            ON sc.object_id = ic.object_id 
           AND sc.column_id = ic.column_id 
        WHERE i.indid > 0 
          AND i.indid < 255 
          AND (i.status & 64) = 0 
          AND i.id = @tableid 
          AND i.indid = @indexid 
        ORDER BY 
          i.name, 
          CASE ic.is_included_column 
           WHEN 1 THEN ic.index_column_id 
           ELSE ic.key_ordinal 
          END   
        IF LEN(@isql_key) > 1 
         SET @isql_key = LEFT(@isql_key, LEN(@isql_key) -1) 
    
        IF LEN(@isql_incl) > 1 
         SET @isql_incl = LEFT(@isql_incl, LEN(@isql_incl) -1)    
        SET @srsql = 'CREATE ' + 'INDEX [' + @indexname + ']' + ' ON [' + @tablename 
         + '] '   
        SET @srsql = @srsql + '(' + @isql_key + ')'    
        IF (@isql_incl <> '') 
         SET @srsql = @srsql + ' INCLUDE(' + @isql_incl + ')'    
        IF (@indexfillfactor <> 0) 
          SET @srsql = @srsql + ' WITH (FILLFACTOR = ' + CONVERT(VARCHAR(10), @indexfillfactor) 
          + ')'    
        FETCH NEXT FROM index_cursor INTO @tablename,@tableid,@indexid,@indexname, 
        @isunique ,@isclustered , @indexfillfactor   
        INSERT INTO @ScriptsRetorno 
        VALUES 
         (@srsql) 
    END 
    CLOSE index_cursor 
    DEALLOCATE index_cursor 
    SELECT * 
    FROM @ScriptsRetorno 
    RETURN @@ERROR 
    END 
    
Problemi correlati