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