Як змінити тип даних колонки даних у таблиці даних?


120

У мене є:

DataTable Table = new DataTable;
SqlConnection = new System.Data.SqlClient.SqlConnection("Data Source=" + ServerName + ";Initial Catalog=" + DatabaseName + ";Integrated Security=SSPI; Connect Timeout=120");

SqlDataAdapter adapter = new SqlDataAdapter("Select * from " + TableName, Connection);
adapter.FillSchema(Table, SchemaType.Source);
adapter.Fill(Table);

DataColumn column = DataTable.Columns[0];

Що я хочу зробити:

Припустимо, що в даний час стовпчик.DataType.Name - "Подвійний" . Я хочу, щоб це стало "Int32" .

Як я цього досягти?

Відповіді:


269

Ви не можете змінити Тип даних після заповнення даних. Однак ви можете клонувати таблицю даних, змінити тип стовпця та завантажити дані з попередньої таблиці даних у таблицю клонованих, як показано нижче.

DataTable dtCloned = dt.Clone();
dtCloned.Columns[0].DataType = typeof(Int32);
foreach (DataRow row in dt.Rows) 
{
    dtCloned.ImportRow(row);
}

Любіть рішення - Елегантний! Однак ImportRow (), здається, не перетворює рядкові значення в плаваючі значення для мене. Я щось тут пропускаю?
yangli.liy

1
DataTable.ImportRow () (а точніше його основне сховище) перетворює значення за допомогою інтерфейсу IConvertible. Переконайтесь у встановленні властивості DataTable.Locale відповідно! (за замовчуванням - CultureInfo.CurrentCulture)
сер убив лот

Це не працює. Я намагаюся створити стовпець пам'яті замість колони масиву байтів, що надходять до db. Це дає System.ArgumentException: Тип значення має невідповідність типу type стовпця. Не вдалося зберегти <System.Byte []> у стовпці DATA. Очікуваний тип - MemoryStream
Mert Serimer

28

Хоча це правда, що ви не можете змінити тип стовпця після DataTableзаповнення, ви можете змінити його після виклику FillSchema, але перед тим, як дзвонити Fill. Наприклад, скажімо, третій стовпець, який ви хочете конвертувати doubleв Int32, ви можете використовувати:

adapter.FillSchema(table, SchemaType.Source);
table.Columns[2].DataType = typeof (Int32);
adapter.Fill(table);

1
Зверніть увагу, що це, здається, не працює, якщо команда вашого адаптера - це збережена процедура
Марк Совул

1
Я перевірив це за Oracle.MangedDataAccess.Client.OracleDataAdapterдопомогою збереженої процедури. Це працює. Дякую.
3пер

21

Стара публікація, але я думав, що я зважую, з розширенням DataTable, яке може конвертувати окремий стовпець за один раз, у заданий тип:

public static class DataTableExt
{
    public static void ConvertColumnType(this DataTable dt, string columnName, Type newType)
    {
        using (DataColumn dc = new DataColumn(columnName + "_new", newType))
        {
            // Add the new column which has the new type, and move it to the ordinal of the old column
            int ordinal = dt.Columns[columnName].Ordinal;
            dt.Columns.Add(dc);
            dc.SetOrdinal(ordinal);

            // Get and convert the values of the old column, and insert them into the new
            foreach (DataRow dr in dt.Rows)
                dr[dc.ColumnName] = Convert.ChangeType(dr[columnName], newType);

            // Remove the old column
            dt.Columns.Remove(columnName);

            // Give the new column the old column's name
            dc.ColumnName = columnName;
        }
    }
}

Потім це можна назвати так:

MyTable.ConvertColumnType("MyColumnName", typeof(int));

Звичайно, використовуючи потрібний тип, доки кожне значення стовпця може бути перетворене на новий тип.


Помилка "Об'єкт повинен реалізувати IConvertible." при перетворенні байт [] в стовпчик типу рядка.
Харшад Векарія

Просто додайте до нього кілька дженериків public static void ConvertColumnType<T>(this DataTable dt, string columnName, TnewType) where T : Type, IConvertible
ТМ

3
Я думаю, що це найелегантніше рішення, просто уникайте конвертування DBNulls, наприклад dr[dc.ColumnName] = dr[columnName] == DBNull.Value ? DBNull.Value : Convert.ChangeType(dr[columnName], newType);
miboper

8

Розглянемо також зміну типу повернення:

select cast(columnName as int) columnName from table

8
Dim tblReady1 As DataTable = tblReady.Clone()

'' convert all the columns type to String 
For Each col As DataColumn In tblReady1.Columns
  col.DataType = GetType(String)
Next

tblReady1.Load(tblReady.CreateDataReader)

8

Я трохи інший підхід. Мені потрібно було проаналізувати дату з імпорту Excel, який був у форматі дати OA. Цю методологію досить просто побудувати з ... по суті,

  1. Додайте потрібний стовпець
  2. Прокопайте рядки, перетворюючи значення
  3. Видаліть оригінальний стовпець та перейменуйте його на новий, щоб відповідати старому

    private void ChangeColumnType(System.Data.DataTable dt, string p, Type type){
            dt.Columns.Add(p + "_new", type);
            foreach (System.Data.DataRow dr in dt.Rows)
            {   // Will need switch Case for others if Date is not the only one.
                dr[p + "_new"] =DateTime.FromOADate(double.Parse(dr[p].ToString())); // dr[p].ToString();
            }
            dt.Columns.Remove(p);
            dt.Columns[p + "_new"].ColumnName = p;
        }

Дякую! Саме те, що я шукав.
Рахул Сінгх

4

Після DataTableзаповнення анекдоту ви не можете змінити тип стовпця.

Найкращий варіант у цьому сценарії - додати Int32стовпець до DataTableпопереднього заповнення:

dataTable = new DataTable("Contact");
dataColumn = new DataColumn("Id");
dataColumn.DataType = typeof(Int32);
dataTable.Columns.Add(dataColumn);

Тоді ви можете клонувати дані з початкової таблиці до нової таблиці:

DataTable dataTableClone = dataTable.Clone();

Ось допис з більш детальною інформацією .


4

якщо ви хочете змінити лише стовпець. Для прикладу від рядка до int32 ви можете використовувати властивість Expression:

DataColumn col = new DataColumn("col_int" , typeof(int));
table.Columns.Add(col);
col.Expression = "table_exist_col_string"; // digit string convert to int  

хороша відповідь! не потрібно передбачати (...) і перевіряти нульові значення.
Бехзад Ебрагімі,

2

Я створив функцію розширення, яка дозволяє змінювати тип стовпця DataTable. Замість того, щоб клонувати всю таблицю та імпортувати всі дані, вона просто клонує стовпчик, аналізує значення та видаляє оригінал.

    /// <summary>
    /// Changes the datatype of a column. More specifically it creates a new one and transfers the data to it
    /// </summary>
    /// <param name="column">The source column</param>
    /// <param name="type">The target type</param>
    /// <param name="parser">A lambda function for converting the value</param>
    public static void ChangeType(this DataColumn column, Type type, Func<object, object> parser)
    {
        //no table? just switch the type
        if (column.Table == null)
        {
            column.DataType = type;
            return;
        }

        //clone our table
        DataTable clonedtable = column.Table.Clone();

        //get our cloned column
        DataColumn clonedcolumn = clonedtable.Columns[column.ColumnName];

        //remove from our cloned table
        clonedtable.Columns.Remove(clonedcolumn);

        //change the data type
        clonedcolumn.DataType = type;

        //change our name
        clonedcolumn.ColumnName = Guid.NewGuid().ToString();

        //add our cloned column
        column.Table.Columns.Add(clonedcolumn);

        //interpret our rows
        foreach (DataRow drRow in column.Table.Rows)
        {
            drRow[clonedcolumn] = parser(drRow[column]);
        }

        //remove our original column
        column.Table.Columns.Remove(column);

        //change our name
        clonedcolumn.ColumnName = column.ColumnName;
    }
}

Ви можете використовувати його так:

List<DataColumn> lsColumns = dtData.Columns
    .Cast<DataColumn>()
    .Where(i => i.DataType == typeof(decimal))
    .ToList()

//loop through each of our decimal columns
foreach(DataColumn column in lsColumns)
{
    //change to double
    column.ChangeType(typeof(double),(value) =>
    {
        double output = 0;
        double.TryParse(value.ToString(), out output);
        return output;  
    });
}

Вищевказаний код змінює всі десяткові стовпці на подвійні.


1
DataTable DT = ...
// Rename column to OLD:
DT.Columns["ID"].ColumnName = "ID_OLD";
// Add column with new type:
DT.Columns.Add( "ID", typeof(int) );
// copy data from old column to new column with new type:
foreach( DataRow DR in DT.Rows )
{ DR["ID"] = Convert.ToInt32( DR["ID_OLD"] ); }
// remove "OLD" column
DT.Columns.Remove( "ID_OLD" );

1

Я поєднав ефективність рішення Марка - так що я не маю до .Cloneвсієї DataTable - із загальною характеристикою та розширенням, тому я можу визначити власну функцію перетворення . Ось що я закінчив:

/// <summary>
///     Converts a column in a DataTable to another type using a user-defined converter function.
/// </summary>
/// <param name="dt">The source table.</param>
/// <param name="columnName">The name of the column to convert.</param>
/// <param name="valueConverter">Converter function that converts existing values to the new type.</param>
/// <typeparam name="TTargetType">The target column type.</typeparam>
public static void ConvertColumnTypeTo<TTargetType>(this DataTable dt, string columnName, Func<object, TTargetType> valueConverter)
{
    var newType = typeof(TTargetType);

    DataColumn dc = new DataColumn(columnName + "_new", newType);

    // Add the new column which has the new type, and move it to the ordinal of the old column
    int ordinal = dt.Columns[columnName].Ordinal;
    dt.Columns.Add(dc);
    dc.SetOrdinal(ordinal);

    // Get and convert the values of the old column, and insert them into the new
    foreach (DataRow dr in dt.Rows)
    {
        dr[dc.ColumnName] = valueConverter(dr[columnName]);
    }

    // Remove the old column
    dt.Columns.Remove(columnName);

    // Give the new column the old column's name
    dc.ColumnName = columnName;
}

Таким чином, використання набагато простіше, а також настроюється:

DataTable someDt = CreateSomeDataTable();
// Assume ColumnName is an int column which we want to convert to a string one.
someDt.ConvertColumnTypeTo<string>('ColumnName', raw => raw.ToString());

0

Puedes agregar una columna con tipo de dato distto, luego copiar los datos y eliminar la columna anterior

TB.Columns.Add("columna1", GetType(Integer))    
TB.Select("id=id").ToList().ForEach(Sub(row) row("columna1") = row("columna2"))    
TB.Columns.Remove("columna2")

1
Ви повинні додати певний контекст, щоб відповісти, лише відповідь на код не має значення, оскільки вони сприймаються як низька якість.
Naught Nabi Zada
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.