Как изменить тип данных столбца данных в таблице данных?


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

Что я хочу сделать:

Предположим, что в настоящее время column.DataType.Name имеет значение «Double» . Я хочу, чтобы он стал «Int32» .

Как мне этого добиться?

Ответы:


269

Вы не можете изменить DataType после того, как Datatable будет заполнен данными. Однако вы можете клонировать таблицу данных, изменить тип столбца и загрузить данные из предыдущей таблицы данных в клонированную таблицу, как показано ниже.

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)
Sir Kill A Lot

Это не работает. Я пытаюсь создать столбец потока памяти вместо столбца массива байтов db. Это дает System.ArgumentException: тип значения не соответствует типу столбца. Не удалось сохранить <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». при преобразовании Byte [] в столбец строкового типа.
Harshad Vekariya

Просто добавьте к нему несколько дженериков public static void ConvertColumnType<T>(this DataTable dt, string columnName, TnewType) where T : Type, IConvertible
TM

3
Я думаю, что это наиболее элегантное решение, просто избегайте преобразования DBNull, например 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  

хороший ответ! нет необходимости использовать foreach (...) и проверять нулевые значения.
Behzad Ebrahimi

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

Пуэдес аргар уна колумна кон типо де дато особняк, луэго копиар лос датос и элиминар ла колумна передний

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

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