2009-08-11 9 views
14

Ich habe eine smalldatetime-Spalte, die ich ändern muss, um eine Datetime-Spalte zu sein. Dies ist Teil eines Installationsprozesses und kann daher nicht manuell durchgeführt werden. Leider hat die Spalte einige Indizes und eine Nicht-Null-Einschränkung. Die Indizes sind leistungsbezogen und müssen nur unter Verwendung des neuen Datentyps beibehalten werden. Ist es möglich, eine Aussage zu schreiben, die es mir erlaubt, die relevanten Informationen beizubehalten und gleichzeitig den Spaltentyp zu ändern? Wenn ja, wie kann dies getan werden?SQL-Indizes beim Ändern des Spaltentyps erhalten

Antwort

10

Sie können den Datentyp von small mit den Indizes in Datetime ändern, eindeutige Einschränkungen, Fremdschlüssel-Constraints oder überprüfen Einschränkungen vorhanden. Sie müssen alle löschen, bevor Sie den Typ ändern. Dann:

alter table T alter column TestDate datetime not null 

Dann neu erstellen Sie die Einschränkungen und Indizes, die noch gelten.


Einige unterschiedliche Ansätze für den Abfall zu erzeugen und erzeugt:

1) Wenn Sie auf alle Indizes und Constraints explizite Namen gegeben haben, dann mit Ihrem Installateur ein statisches Skript in jeder Umgebung ausgeführt werden können (dev, test, Benutzerakzeptanztests, Leistungstest usw. Produktion.)

Um dieses explizite Skript zu erstellen, können Sie: a) Verwenden Sie SSMS (oder mit SQL Server 2000, Enterprise Manager), um die Erstellungs- und die Anweisung zu senden. b) Arbeiten Sie von Ihrem Quellcode-Repository aus, um die Namen und Definitionen der abhängigen Objekte zu ermitteln und das entsprechende statische Skript zusammenzustellen. c) Versuch, die alter-Anweisung auszuführen. Sehen Sie, worauf es scheitert. Nachschlagen Sie die Definitionen und schreiben Sie den Tropfen und erstellen Sie ihn. (Persönlich wäre das großartig, um den Tropfen zu schreiben, nicht so gut beim Erstellen.)

2) Wenn Sie nicht allen Indizes und Einschränkungen explizite Namen gegeben haben, muss Ihr Installer das Data Dictionary nach den entsprechenden Namen abfragen und Dynamic SQL verwenden, um die Drops in der richtigen Reihenfolge auszuführen die alter column-Anweisung und dann das Erstellen in der richtigen Reihenfolge nach der alter-Spalte.

Dies ist einfacher, wenn Sie wissen, dass es keine Einschränkungen und nur Indizes gibt.

Es kann Werkzeuge oder Bibliotheken geben, die bereits wissen, wie das geht.

Wenn dies eine gepackte Anwendung ist, können Sie nicht sicher sein, dass die lokalen DBAs keine Indizes hinzugefügt haben.

HINWEIS: Wenn eine eindeutige Einschränkung vorhanden ist, wird ein Index erstellt, den Sie nicht mit DROP INDEX löschen können.

+0

Ich konnte die Quelle sql hinter der Erstellung der Indizes finden und war in der Lage, diese zu verwenden, um diese zu löschen, bevor der Typ geändert wurde. Was die Einschränkung betrifft, habe ich eine Abfrage über Google gefunden, die verwendet werden kann, um den zufällig generierten Constraint-Namen zu bestimmen. –

+0

Deklarieren @constraintName als nvarchar (100) Deklarieren @sql nvarchar (1000) \t select @constraintName = O.name \t aus sysobjects AS O \t links sysobjects AS T \t \t auf O.parent_obj = T. beitreten ID \t wo isnull (objectproperty (O.id, 'IsMSShipped'), 1) = 0 \t \t und O.Name nicht gefallen '% dtproper%' \t \t O.name und nicht wie 'dt [_]%' \t \t und T.name = 'MyTable' \t \t und O.name wie 'DF__MyTabl__MyCol%' \t wenn nicht @constraintName null ist \t beginnen \t \t wählen @sql = 'ALTER TABLE [MyTable] DROP CONSTRAINT [' + @constraintName + ']' \t \t ausführen sp_executesql @sql \t Ende –

4

BEARBEITEN: Es hängt vom ursprünglichen und geänderten Datentyp ab. Wenn Sie versuchen, eine Spalte von varchar in nvarchar zu ändern, schlägt sie fehl. Wenn Sie hingegen die Spalte von varchar (16) zu varchar (32) ändern, wird es erfolgreich sein.

--Disable Index 
ALTER INDEX MyIndex ON MyTable DISABLE 
GO 

-- Change column datatype 

--Enable Index 
ALTER INDEX MyIndex ON MyTable REBUILD 
GO 

Wenn Sie den Typ einer Spalte zu ändern, dann werden alle Indizes, die diese Spalte verwenden müssen neu aufgebaut werden.

Aber wenn Sie keine riesigen Datenmengen haben (oder 24/7 laufen), ist der Neuaufbau von Indizes keine große Sache. Planen Sie einfach ein Wartungsfenster.

+0

Was gut wäre, denke ich, für Phillip, solange sie dabei bleiben und mit dem neuen automatisch neu aufgebaut werden Datentyp. – Thilo

+0

Wenn ich versuche, und führen Sie ALTER INDEX [MyIndex] auf [MyTable] SPERREN ich den Fehler „falsche Syntax nahe dem Schlüsselwort‚INDEX‘zu bekommen. Bin ich etwas fehlt? –

+0

Da dieser Kommentar verfasst ich, dass SQL Server gefunden haben 2000 erlaubt Ihnen nicht, einen Index zu deaktivieren.Es muss gelöscht und erstellt werden.Wir haben ein paar Server, die immer noch auf SQL Server 2000 sind, so dass der Befehl Deaktivieren für mich nicht funktioniert. –

6

Wenn Sie nur die Größe ändern, bleibt der Index weiterhin auf dem Tisch.

Wenn Sie den Datentyp ändern, erhalten Sie eine Fehlermeldung, dass Objekte von der Spalte abhängig sind, die Sie ändern möchten. Daher können Sie sie nicht ändern.

Sie können die betreffenden Indexe manuell oder per Skript scripten. Klicken Sie in SSMS mit der rechten Maustaste auf die Tabelle, und scripten Sie das betreffende Objekt aus.

Wenn Sie programmatisches Index-Scripting wollen, hier ist ein gespeicherter proc, den ich benutzt habe, den ich von einem ehemaligen Kollegen von mir bekommen habe.

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

Dies ist ein sehr nützlicher SP, unterscheidet jedoch nicht zwischen Spalten in Index- und eingeschlossenen Spalten und bedroht auf diese Weise alle Spalten als gleich. –

0

Am besten erstellen Sie eine Prozedur, die das Indexskript einer bestimmten Tabelle/Spalte zurückgibt. So können Sie die Indizes nur aus der zu ändernden Spalte und nicht alle Indizes aus der Tabelle entfernen, während das Erstellen von Indizes etwas teuer sein kann.

  1. Speichert das Ergebnis des Verfahrens in einer Datentabelle
  2. Löschen Sie die Indizes der Spalte
  3. Ändern Sie bitte Ihre Spalte
  4. die Indizes in der Datentabelle

    gespeichert Rebuild
    -- 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 
    
Verwandte Themen