2009-08-11 93 views
14

我有一個smalldatetime列,我需要將其更改爲datetime列。這是安裝過程的一部分,因此它不能是手動過程。不幸的是,該列有一些索引和一個非空約束。索引與性能相關,只需使用新的數據類型即可保留。是否有可能編寫一個聲明,使我可以在保留相關信息的同時更改列數據類型?如果是這樣,這怎麼辦?在更改列數據類型時保留SQL索引

回答

10

您不能使用索引,唯一約束,外鍵約束或檢查約束將數據類型從smalldatetime更改爲datetime。在更改類型之前,您必須先刪除它們。然後:

alter table T alter column TestDate datetime not null 

然後重新創建仍然適用的約束和索引。


產生下降一些不同的方法,並創建:

1)如果你給了明確的名稱所有的索引和約束那麼你的安裝程序可以運行在每個環境(靜態腳本開發,測試,用戶驗收測試,性能測試等,生產)。

要生成此顯式腳本,您可以: a)使用SSMS(或與SQL Server 2000企業管理器一起)編寫創建和刪除語句的腳本。 b)從你的源代碼倉庫開始尋找依賴對象的名稱和定義,並將適當的靜態腳本放在一起。 c)嘗試運行alter語句。看看它失敗了。查找定義並手寫下降並創建。 (就個人而言,寫這篇文章會很好,但對創作並不那麼擅長。)

2)如果您尚未給所有索引和約束指定明確的名稱,則安裝程序必須查詢數據字典以獲取適當的名稱,並使用動態SQL以正確的順序在alter column語句,然後按照正確的順序在alter列後創建。

如果你知道沒有約束並且只是索引,這將會更簡單。

可能有工具或庫已經知道如何做到這一點。另外,如果這是一個打包的應用程序,您可能無法確定本地DBA沒有添加索引。

注意:如果存在唯一約束,它將構建一個索引,您將無法使用DROP INDEX刪除該索引。

+0

我能夠在創建索引後找到源sql,並能夠在更改類型之前使用它們來刪除這些索引。至於約束,我通過google發現了一個查詢,可以用來確定隨機生成的約束名稱。 – 2009-08-11 15:46:55

+0

申報@constraintName如爲nvarchar(100) 聲明@sql爲nvarchar(1000) \t選擇@constraintName = O.name \t從系統對象如鄰 \t左加入系統對象AS T上O.parent_obj = T. \t \t id \t其中isnull(objectproperty(O.id,'IsMSShipped'),1)= 0 \t \t和O.名不喜歡 '%dtproper%' \t \t和O.name不喜歡 'DT [_]%' \t \t和T.name = 'MyTable的' \t \t和O.name像 'DF__MyTabl__MyCol%' \t如果不是@constraintName爲null \t開始 \t \t選擇@sql = 'ALTER TABLE [MyTable的] DROP CONSTRAINT [' + @constraintName + ']' \t \t執行sp_executesql的@sql \t端 – 2009-08-11 15:47:32

4

編輯:它取決於原始和更改的數據類型。 如果嘗試將varchar列更改爲nvarchar,則該列將失敗。 而如果將varchar(16)中的列更改爲varchar(32),則它將成功。

--Disable Index 
ALTER INDEX MyIndex ON MyTable DISABLE 
GO 

-- Change column datatype 

--Enable Index 
ALTER INDEX MyIndex ON MyTable REBUILD 
GO 

如果更改列的類型,那麼使用該列的所有索引都必須重新構建。

但是,除非您有大量數據(或全天候運行),否則重建索引是沒有什麼大不了的。只需安排一個維護窗口。

+0

指標對於菲利普來說,我相信這很好,只要他們堅持使用新的設備就可以自動重建數據類型。 – Thilo 2009-08-11 03:54:07

+0

當我嘗試運行 ALTER INDEX [myIndex]對[MyTable的] DISABLE 我得到錯誤「附近關鍵字‘INDEX’的語法不正確。我缺少的東西? – 2009-08-11 14:58:13

+0

自發布此評論我發現,SQL服務器2000不允許你禁用一個索引,它必須被刪除和創建,我們有一些仍在SQL Server 2000上的服務器,因此禁用命令對我來說不起作用 – 2009-08-11 15:11:46

6

如果您只是更改大小,索引仍將保留在表格中。

如果您要更改數據類型,那麼您將收到一條錯誤消息,指出對象取決於您要更改的列,因此您將無法對其進行更改。

您可以通過手動或通過腳本來編寫問題索引。在SSMS中,右鍵單擊表格並腳本化所討論的對象。

如果你想要編程索引腳本,這是一個存儲過程,我一直在使用我從我的同事那裏得到的。

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

這是非常有用的SP,但它並不區分索引和包含列中的列,並以此方式威脅所有列相同。 – 2012-11-01 13:25:52

0

最好的辦法是創建一個返回給定表/列的索引腳本的過程。所以你可以從被修改的列中刪除索引,而不是從表中刪除所有索引,而創建索引可能會有些貴。

  1. 存儲過程的一個DataTable
  2. 結果中刪除列
  3. 的索引修改您的列
  4. 重建存儲在數據表

    -- 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