2009-08-11 7 views
14

J'ai une colonne smalldatetime que j'ai besoin de modifier pour être une colonne datetime. C'est quelque chose qui fera partie d'un processus d'installation, il ne peut donc pas être une procédure manuelle. Malheureusement, la colonne a quelques index et une contrainte non nulle dessus. Les index sont liés aux performances et doivent être conservés uniquement à l'aide du nouveau type de données. Est-il possible d'écrire une déclaration qui me permettra de conserver les informations pertinentes tout en modifiant le type de données de la colonne? Si cela est le cas, comment cela peut-il être fait?Préserver les index SQL lors de la modification du type de données de colonne

Répondre

10

Vous ne pouvez pas modifier le type de données smalldatetime datetime avec les index, les contraintes uniques, les clés étrangères ou les contraintes de vérification en place. Vous devrez les laisser tomber avant de changer le type. Puis:

alter table T alter column TestDate datetime not null 

Recréez ensuite les contraintes et les index qui s'appliquent toujours.


Quelques approches différentes pour générer la chute et crée:

1) Si vous avez donné des noms explicites à tous les index et les contraintes, votre installateur peut exécuter un script statique dans chaque environnement (dev, test, Pour générer ce script explicite, vous pouvez: a) Utiliser SSMS (ou avec SQL Server 2000, gestionnaire d'entreprise) pour créer un script pour les instructions create et drop. b) Travaillez à partir de votre référentiel de code source pour découvrir les noms et définitions des objets dépendants et assembler le script statique approprié. c) Essayez d'exécuter l'instruction alter. Voir ce qu'il échoue. Rechercher les définitions et écrire à la main la goutte et créer. (Personnellement, ce serait génial pour écrire la goutte, pas si bon à la création.2) Si vous n'avez pas donné de noms explicites à tous les index et contraintes, votre installateur devra interroger le dictionnaire de données pour les noms appropriés et utiliser le SQL dynamique pour exécuter les gouttes, dans le bon ordre, avant l'instruction alter column, puis crée, dans le bon ordre, après la colonne alter.

Cela sera plus simple si vous savez qu'il n'y a pas de contraintes, et seulement des index.

Il existe peut-être des outils ou des bibliothèques qui savent déjà comment procéder.

En outre, s'il s'agit d'une application empaquetée, vous ne pouvez pas être assuré que les DBA locaux n'ont pas ajouté d'index. REMARQUE: S'il existe une contrainte unique, il aura généré un index que vous ne pourrez pas supprimer avec DROP INDEX.

+0

J'ai été capable de trouver la source sql derrière la création des index et j'ai pu les utiliser pour les supprimer avant de changer le type. En ce qui concerne la contrainte, j'ai trouvé une requête via google qui peut être utilisée pour déterminer le nom de la contrainte générée aléatoirement. –

+0

déclarer @constraintName comme nvarchar (100) déclarer @sql nvarchar (1000) \t select @constraintName = O.name \t de sysobjects AS O \t jointure gauche sysobjects AS T \t \t sur O.parent_obj = T. id \t où est nul (objectproperty (O.id, 'IsMSShipped'), 1) = 0 \t \t et O.nom pas comme '% dtproper%' \t \t et O.name pas comme 'dt [_]%' \t \t et T.name = 'MyTable' \t \t et O.name comme 'DF__MyTabl__MyCol%' \t sinon @constraintName est nulle \t commencent \t \t select @sql = 'ALTER TABLE [MyTable] DROP CONSTRAINT [' + @constraintName + ']' \t \t execute sp_executesql @sql \t fin –

4

EDIT: Cela dépend de l'original et du type de données modifié. Si vous essayez de modifier une colonne de varchar en nvarchar, elle échouera. Considérant que, si vous modifiez la colonne de varchar (16) à varchar (32), il réussira.

--Disable Index 
ALTER INDEX MyIndex ON MyTable DISABLE 
GO 

-- Change column datatype 

--Enable Index 
ALTER INDEX MyIndex ON MyTable REBUILD 
GO 

Si vous modifiez le type d'une colonne, tous les index utilisant cette colonne devront être reconstruits. Mais à moins que vous n'ayez d'énormes volumes de données (ou que vous les exécutiez 24 heures sur 24, 7 jours sur 7), la reconstruction des index n'est pas un gros problème. Juste planifier une fenêtre de maintenance.

+0

Ce qui irait bien, je suppose, pour Phillip, tant qu'ils restent et reconstruit automatiquement en utilisant le nouveau Type de données. – Thilo

+0

Lorsque je tente et exécuter ALTER INDEX [MYIndex] sur [MyTable] DISABLE je reçois l'erreur « syntaxe incorrecte près du mot-clé « INDEX ». Me manque quelque chose? Je –

+0

Depuis de poster ce commentaire que j'ai trouvé que SQL Server 2000 ne vous permet pas de désactiver un index.Il doit être supprimé et créé.Nous avons quelques serveurs qui sont encore sur SQL Server 2000. Donc la commande de désactivation ne fonctionnera pas pour moi –

6

Si vous modifiez simplement la taille, l'index restera sur la table.

Si vous modifiez le type de données, un message d'erreur s'affiche indiquant que les objets dépendent de la colonne que vous essayez de modifier et que vous ne pourrez donc pas la modifier.

Vous pouvez écrire les index en question manuellement ou via un script. Dans SSMS, cliquez avec le bouton droit sur la table et écrivez l'objet en question.

Si vous voulez un script d'indexation programmatique, voici un proc stocké que j'ai utilisé avec un de mes anciens collègues.

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

Ceci est très utile SP, mais il ne fait pas de distinction entre les colonnes dans l'index et les colonnes incluses et menace ainsi toutes les colonnes comme étant les mêmes. –

0

La meilleure chose à faire est de créer une procédure qui retourne le script d'index d'une table/colonne donnée. Vous pouvez donc supprimer les index seulement de la colonne en cours de modification et pas tous les index de la table, tandis que la création d'index peut être un peu coûteuse.

  1. stocke le résultat de la procédure dans un datatable
  2. Supprimer les indices de la colonne
  3. Modifier votre colonne
  4. Reconstruire les index stockés dans 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 
    
Questions connexes