วิธีการเปลี่ยน DataType ของ DataColumn ใน DataTable


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เป็น"คู่" ฉันต้องการให้มันกลายเป็น"Int32" "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

สิ่งนี้ใช้ไม่ได้ ฉันกำลังพยายามสร้างคอลัมน์ memorystream แทนคอลัมน์ db coming byte array ให้ System.ArgumentException: ประเภทของค่าไม่ตรงกันกับประเภทคอลัมน์ไม่สามารถจัดเก็บ <System Byte []> ในคอลัมน์ข้อมูล ประเภทที่คาดไว้คือ MemoryStream
Mert Serimer

28

ในขณะที่มันเป็นความจริงที่คุณไม่สามารถเปลี่ยนชนิดของคอลัมน์หลังจากที่DataTableเต็มไปคุณสามารถเปลี่ยนได้หลังจากที่คุณโทรหาแต่ก่อนที่คุณเรียกFillSchema Fillตัวอย่างเช่นสมมติว่าคอลัมน์ที่ 3 เป็นคอลัมน์ที่คุณต้องการแปลงdoubleเป็นInt32คุณสามารถใช้:

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

1
โปรดทราบว่าสิ่งนี้จะไม่ทำงานหากคำสั่งของอะแดปเตอร์ของคุณเป็นกระบวนงานที่เก็บไว้
Mark Sowul

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

แน่นอนว่าจะใช้ประเภทใดก็ได้ที่คุณต้องการตราบใดที่แต่ละค่าในคอลัมน์สามารถแปลงเป็นประเภทใหม่ได้


ให้ข้อผิดพลาด "Object must ใช้ IConvertible" ในขณะที่แปลง Byte [] เป็นคอลัมน์ประเภทสตริง
Harshad Vekariya

เพียงเพิ่มชื่อสามัญลงไป public static void ConvertColumnType<T>(this DataTable dt, string columnName, TnewType) where T : Type, IConvertible
TM

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

ขอบคุณ! สิ่งที่ฉันกำลังมองหา
Rahul Singh

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 (... )และตรวจสอบค่า null
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

ฉันรวมประสิทธิภาพของโซลูชันของ Mark - ดังนั้นฉันจึงไม่จำเป็นต้อง.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 distinto, 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
คุณควรเพิ่มบริบทบางอย่างให้กับคุณคำตอบรหัสเท่านั้นไม่มีค่าเนื่องจากถูกมองว่ามีคุณภาพต่ำ
Nawed Nabi Zada
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.