2017-10-09 2 views
3

이것은 언덕만큼 오래되었지만 거의 모든 사람이 그 문제를 겪었습니다. 그래서, 나는 가장 일반적이며 가장 견고한 해결책을 찾고 있습니다.SQL Server - SQL Cascade 변경 데이터 형식의 열

테이블에 기본 키 열이있는 경우 다른 테이블에 해당 열과 연결된 외래 키가 있으며이 열의 데이터 형식을 변경하려는 경우 쓸 필요없이이를 수행하는 가장 좋은 도구는 무엇입니까? 임시 데이터베이스 스크립트를 수동으로, 그리고 데이터를 삭제할 필요없이? 이 도구가 있습니까?

따라서, 예를 들어, 나는 두 개의 테이블

판매

SaleKey (INT)

총 (10 진수)

SaleLine

SaleLineKey이 말 (int)

판매 (int)를 는 < - 다시 외래 키는

수량 (int)를 판매하는

비율 (10 진수)

내가 도매 변화에 판매에 열 SaleKey을 원하는 말할 수 있습니다 고유 한 식별자로 즉, 새로운 고유 식별자 열을 Sale에 추가하고, SaleLine에 유사한 열을 추가하고, SaleLine의 데이터를 Sale을 반영하도록 업데이트하고, 외래 키와 기본 키를 삭제 한 다음 새 외래 키를 넣는 데이터베이스 스크립트를 작성해야합니다. 기본 키를 누릅니다. 이것 모두 가능하지만 시간이 많이 걸립니다.

나를 위해이 작업을 수행 할 응용 프로그램이 있습니까? 또는 누군가가 이미 작성한 저장된 proc? SQL Server에는 데이터 캐스케이드 삭제가 있습니다. 캐스케이드 변경 데이터 유형은 무엇입니까?

답변

0

이것은 C#을 사용하는 솔루션입니다. ExecuteScript 및 ExecuteScalar와 관련하여 공백을 채워야하지만 SQL Server를 사용하는 한 논리는 작동합니다. 이 경우 uniqueidentifier로 변환되지만 다른 데이터 유형으로 변경하는 것은 어렵지 않습니다. 여기에는 몇 가지 가정이 있습니다. 예를 들어,이 코드는 변경하려는 열에 최대 하나의 기본 키가 있다고 가정합니다. 어떤 시점에서 이것을 오픈 소스 저장소로 옮기는 것이 좋은 프로젝트 일 수 있습니다. 또한 T/SQL로 쉽게 변환 될 수 있습니다. 나는 T/SQL보다 C#에서 더 낫다.

경고 : 이것은 파괴적인 코드입니다. 색인 및 키 등을 무차별 적으로 삭제합니다. 따라서 결과 컬럼이 의도 한 데이터 스키마와 일치하는지 확인하기 위해이를 실행할 때주의해야합니다. 이것은 방금 작업을 수행하기위한 도구 일 뿐이며 모든 솔루션을 방탄하는 것은 아닙니다. 이것은 우리 데이터베이스에서 상당히 철저하게 테스트되었습니다.

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}]"); 
      } 
     } 
    }