2017-10-09 74 views
3

這是一個和山丘一樣古老的問題,幾乎每個人都會遇到它。所以,我正在尋找最常見,最穩固的解決方案。SQL Server - SQL級聯更改列的數據類型

如果我有一個表中的主鍵列,其他表有外鍵鏈接到該列,我想改變列的數據類型,什麼是最好的工具來實現這一點,而不必寫ad hoc數據庫腳本手動,而不必刪除數據? 有沒有這個工具?

所以,比方說,我有兩個表

SaleKey(INT)

總(十進制)

SaleLine

SaleLineKey (int)

銷售(INT)< - 外鍵返回到銷售

數量(INT)

率(十進制)

比方說,我想批發變化對銷售列SaleKey到一個唯一的標識符。這意味着我必須編寫一個數據庫腳本來在Sale上添加新的唯一標識符列,在SaleLine上添加類似的列,更新SaleLine中的數據以反映Sale,刪除外鍵和主鍵,然後將新的外鍵和主鍵主鍵上。這是所有可能的但費時。

是否有一個應用程序在那裏會爲我做這個?或者,有人已經寫過的存儲過程? SQL Server具有級聯刪除數據,級聯更改數據類型如何?

回答

0

這是C#的解決方案。您必須根據ExecuteScript和ExecuteScalar填寫空格,但只要您使用SQL Server,邏輯就會工作。這隻會轉換爲uniqueidentifier,但不會將其更改爲不同的數據類型。這裏有幾個假設。例如,此代碼假定您要更改的列最多隻有一個主鍵。這可能是一個很好的項目,可以在某些時候將其轉化爲開源回購。此外,這可以很容易地轉換爲T/SQL。對於C#,我比T/SQL更好。

警告:這是破壞性的代碼。它將不分青紅皁白地刪除索引和鍵等。因此,在運行時務必小心,以確保生成的列與預期的數據模式匹配。這只是實現手頭任務的一種工具 - 並非萬能的解決方案。儘管在我們的數據庫中已經進行了相當徹底的測試

public void ChangeColumnDataTypeToUniqueIdentifier(string schemaName, string tableName, string columnName, bool allowsNull, ConnectionInfo connectionInfo) 
    { 
     string script = null; 

     var tempColumnName = $"{columnName}Temp"; 

     var columnDataTypeString = DatabaseAdapter.SqlStatementBuilder.GetSqlDataType(ColumnDataType.UniqueIdentifier, 0, 0, 0); 

     //Add the temp column 
     //TODO: We should try to figure out if this needs not null, but we can rely on the schema upgrade to fix this later... 
     ExecuteScript(connectionInfo, 
     "alter table " + 
     $" {schemaName}.{tableName} " + 
     "add " + 
     $" {tempColumnName} {columnDataTypeString}"); 

     var fullTableName = $"[{schemaName}].[{tableName}]"; 

     //Update the temp column to new values 
     //TODO: this contains UniqueIdentifier specific code 
     ExecuteScript(connectionInfo, 
     "update " + 
     $" {fullTableName} " + 
     "set " + 
     $" {tempColumnName} = NEWID()"); 

     ExecuteScript(connectionInfo, 
     "alter table " + 
     $" {schemaName}.{tableName} " + 
     "alter column " + 
     $" {tempColumnName} {columnDataTypeString} {(!allowsNull ? string.Empty : "not")} null"); 

     //Get the schema id of the target table 
     var targetSchemaObjectId = (int)DatabaseAdapter.ExecuteScalar("select schema_id from sys.schemas where name = @Param1", new Collection<DataParameter> { new DataParameter("Param1", schemaName) }, connectionInfo, null); 

     //Get the object id of the table we are modifying 
     var targetTableObjectId = (int)DatabaseAdapter.ExecuteScalar("select object_Id from sys.tables where name = @Param1 and schema_id = @Param2", new Collection<DataParameter> { new DataParameter("Param1", tableName), new DataParameter("Param2", targetSchemaObjectId) }, connectionInfo, null); 

     //Get foreign keys 
     using (var foreignKeyData = DatabaseAdapter.ExecuteReader("select * from Sys.foreign_keys where referenced_object_id = @Param1", new Collection<DataParameter> { new DataParameter("Param1", targetTableObjectId) }, connectionInfo, null)) 
     { 
      //Iterate through foreign keys 
      while (foreignKeyData.DataReader.Read()) 
      { 
       //Get thei object id of the table that references this 
       var tableThatReferencesThisObjectId = (int)foreignKeyData.DataReader["parent_object_Id"]; 
       var foreignKeyObjectId = (int)foreignKeyData.DataReader["object_Id"]; 
       var foreignKeyName = (string)foreignKeyData.DataReader["name"]; 

       //Get the tables data 
       using (var tableThatReferencesThisData = DatabaseAdapter.ExecuteReader("select * from Sys.tables where object_id = @Param1", new Collection<DataParameter> { new DataParameter("Param1", tableThatReferencesThisObjectId) }, connectionInfo, null)) 
       { 

        //Read the record 
        tableThatReferencesThisData.DataReader.Read(); 

        //Get information about the table references this 
        var tableThatReferencesThisName = (string)tableThatReferencesThisData.DataReader["name"]; 
        var tableThatReferencesShemaObjectId = (int)tableThatReferencesThisData.DataReader["schema_id"]; 
        var tableThatReferencesShemaName = (string)DatabaseAdapter.ExecuteScalar("select * from sys.schemas where schema_id = @Param1", new Collection<DataParameter> { new DataParameter("Param1", tableThatReferencesShemaObjectId) }, connectionInfo, null); 

        //Get the name of the column that references the original column 
        var foreignKeyColumnName = (string)DatabaseAdapter.ExecuteScalar 
         (
         "select " + 
         " COL_NAME(fks.parent_object_id, fkcs.parent_column_id) " + 
         "from " + 
         " sys.foreign_keys fks " + 
         "inner join " + 
         " Sys.foreign_key_columns fkcs " + 
         "on " + 
         " fkcs.constraint_object_id = fks.object_id " + 
         "where " + 
         $" fks.object_id = {foreignKeyObjectId}", new Collection<DataParameter> { new DataParameter("Param1", tableThatReferencesShemaObjectId) }, connectionInfo, null); 

        //The new target temp column name 
        var tempForeignKeyColumnName = foreignKeyColumnName + "Temp"; 

        //Concatenate the name of the table that references the original table 
        var tableThatReferencesFullName = $"[{tableThatReferencesShemaName}].[{tableThatReferencesThisName}]"; 

        //Add the temp column 
        //TODO: We should try to figure out if this needs not null, but we can rely on the schema upgrade to fix this later... 
        ExecuteScript(connectionInfo, 
        "alter table " + 
        $" {tableThatReferencesFullName} " + 
        "add " + 
        $" {tempForeignKeyColumnName} uniqueidentifier"); 

        //Update the data in the temp column 
        script = 
        "update " + 
        $" {tableThatReferencesFullName} " + 
        "set " + 
        $"{tempForeignKeyColumnName} = " + 
        " (" + 
        "  select " + 
        $"   {tempColumnName} " + 
        "  from " + 
        $"   {fullTableName} referencedtable " + 
        "  where " + 
        $"   {tableThatReferencesFullName}.[{foreignKeyColumnName}] = referencedtable.{columnName} " + 
        " )"; 
        ExecuteScript(connectionInfo, script); 

        //Drop the original foreign key 
        script = 
        "alter table " + 
        $" {tableThatReferencesFullName} " + 
        "drop " + 
        $" {foreignKeyName} "; 
        ExecuteScript(connectionInfo, script); 

        DropIndexesForTable(tableThatReferencesShemaName, tableThatReferencesThisName, foreignKeyColumnName, connectionInfo); 

        //Drop the old column 
        script = 
        "alter table " + 
        $" {tableThatReferencesFullName} " + 
        "drop column " + 
        $" [{foreignKeyColumnName}] "; 
        ExecuteScript(connectionInfo, script); 

        //Rename the new temp column to the old one 
        ExecuteScript(connectionInfo, $"EXEC sp_rename '{tableThatReferencesFullName}.{tempForeignKeyColumnName}', '{foreignKeyColumnName}', 'COLUMN'"); 
       } 
      } 
     } 

     var pkName = (string)DatabaseAdapter.ExecuteScalar($"select name from sys.key_constraints where parent_object_id = @Param1 and type = 'PK'", new Collection<DataParameter> { new DataParameter("Param1", targetTableObjectId) }, connectionInfo, null); 
     if (!string.IsNullOrEmpty(pkName)) 
     { 
      //Drop the old primary key 
      script = 
      "alter table " + 
      $" {fullTableName} " + 
      "drop " + 
      $" {pkName} "; 
      ExecuteScript(connectionInfo, script); 
     } 

     var defaultConstraintName = (string)DatabaseAdapter.ExecuteScalar(
     "select " + 
     " dc.name " + 
     "FROM " + 
     " SYS.DEFAULT_CONSTRAINTS dc " + 
     "inner join " + 
     " sys.all_columns ac " + 
     "on " + 
     " ac.object_id = @ObjectId and " + 
     " dc.parent_column_id = ac.column_id " + 
     "where " + 
     " parent_object_id = @ObjectId and " + 
     " ac.name = @ColumnName", new Collection<DataParameter> { new DataParameter("ColumnName", columnName), new DataParameter("ObjectId", targetTableObjectId) }, connectionInfo, null); 
     if (!string.IsNullOrEmpty(defaultConstraintName)) 
     { 
      //Drop the old primary key 
      script = 
      "alter table " + 
      $" {fullTableName} " + 
      "drop constraint" + 
      $" {defaultConstraintName} "; 
      ExecuteScript(connectionInfo, script); 
     } 

     DropIndexesForTable(schemaName, tableName, columnName, connectionInfo); 

     //Drop the old column 
     script = 
     "alter table " + 
     $" {fullTableName} " + 
     "drop column " + 
     $" {columnName}"; 
     ExecuteScript(connectionInfo, script); 

     //Rename the new column to the old one 
     ExecuteScript(connectionInfo, $"EXEC sp_rename '{fullTableName}.{tempColumnName}', '{columnName}', 'COLUMN'"); 
    } 

    private void DropIndexesForTable(string schemaName, string tableName, string columnName, ConnectionInfo connectionInfo) 
    { 
     //find indexes dependent on this column 
     string script = "SELECT " + 
     " SchemaName = s.name, " + 
     " TableName = t.name, " + 
     " IndexName = ind.name " + 
     "FROM " + 
     " sys.indexes ind " + 
     "INNER JOIN " + 
     " sys.index_columns ic ON ind.object_id = ic.object_id and ind.index_id = ic.index_id " + 
     "INNER JOIN " + 
     " sys.columns col ON ic.object_id = col.object_id and ic.column_id = col.column_id " + 
     "INNER JOIN " + 
     " sys.tables t ON ind.object_id = t.object_id " + 
     "INNER JOIN " + 
     " sys.schemas s on t.schema_id = s.schema_id " + 
     "WHERE " + 
     " ind.is_primary_key = 0 and" + 
     " s.name = @SchemaName and " + 
     " t.name = @TableName and " + 
     " col.name = @ColumnName"; 
     using (var obstructingIndexData = DatabaseAdapter.ExecuteReader(script, new Collection<DataParameter> { new DataParameter("SchemaName", schemaName), new DataParameter("TableName", tableName), new DataParameter("ColumnName", columnName) }, connectionInfo, null)) 
     { 
      while (obstructingIndexData.DataReader.Read()) 
      { 
       var indexSchema = obstructingIndexData.DataReader["SchemaName"]; 
       var indexTable = obstructingIndexData.DataReader["TableName"]; 
       var IndexName = obstructingIndexData.DataReader["IndexName"]; 

       ExecuteScript(connectionInfo, $"drop index [{indexSchema}].[{indexTable}].[{IndexName}]"); 
      } 
     } 
    } 
相關問題