Wie ändere ich den Datentyp einer Datenspalte in einer Datentabelle?

120

Ich habe:

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

Was ich tun möchte ist:

Angenommen, derzeit column.DataType.Name ist "Double" . Ich möchte, dass es "Int32" wird .

Wie erreiche ich das?

rofans91
quelle

Antworten:

269

Sie können den Datentyp nicht ändern, nachdem die Datentabelle mit Daten gefüllt ist. Sie können jedoch die Datentabelle klonen, den Spaltentyp ändern und Daten aus der vorherigen Datentabelle in die geklonte Tabelle laden, wie unten gezeigt.

DataTable dtCloned = dt.Clone();
dtCloned.Columns[0].DataType = typeof(Int32);
foreach (DataRow row in dt.Rows) 
{
    dtCloned.ImportRow(row);
}
Akhil
quelle
Ich liebe die Lösung - Elegant! ImportRow () scheint jedoch für mich keine Zeichenfolgenwerte in Float-Werte umzuwandeln. Vermisse ich hier etwas?
Yangli.liy
1
DataTable.ImportRow () (oder besser gesagt der zugrunde liegende Speicher) konvertiert Werte mithilfe der IConvertible-Schnittstelle. Stellen Sie sicher, dass Sie die Eigenschaft DataTable.Locale entsprechend festlegen! (Standard ist CultureInfo.CurrentCulture)
Sir Kill A Lot
Das funktioniert nicht. Ich versuche, eine Memorystream-Spalte anstelle einer db kommenden Byte-Array-Spalte zu erstellen. Es gibt eine System.ArgumentException: Der Werttyp stimmt nicht mit dem Spaltentyp überein. <System.Byte []> konnte nicht in der DATA-Spalte gespeichert werden. Der erwartete Typ ist MemoryStream
Mert Serimer
28

Es ist zwar richtig, dass Sie den Typ der Spalte nach dem DataTableAusfüllen nicht ändern können FillSchema, aber Sie können ihn nach dem Aufruf , aber vor dem Aufruf ändern Fill. Zum Beispiel, sagen die dritte Spalte ist , die Sie von konvertieren möchten doublezu Int32, könnten Sie verwenden:

adapter.FillSchema(table, SchemaType.Source);
table.Columns[2].DataType = typeof (Int32);
adapter.Fill(table);
rsbarro
quelle
1
Beachten Sie, dass dies anscheinend nicht funktioniert, wenn der Befehl Ihres Adapters eine gespeicherte Prozedur ist
Mark Sowul
1
Ich habe dies Oracle.MangedDataAccess.Client.OracleDataAdaptermit einer gespeicherten Prozedur getestet . Es funktioniert. Vielen Dank.
3per
21

Alter Beitrag, aber ich dachte, ich würde mit einer DataTable-Erweiterung, die jeweils eine einzelne Spalte konvertieren kann, in einen bestimmten Typ abwägen:

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

Es kann dann so genannt werden:

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

Natürlich mit jedem gewünschten Typ, solange jeder Wert in der Spalte tatsächlich in den neuen Typ konvertiert werden kann.

Marc Ferrold
quelle
Gibt den Fehler "Objekt muss IConvertible implementieren." beim Konvertieren von Byte [] in eine Spalte vom Typ String.
Harshad Vekariya
Fügen Sie einfach einige Generika hinzu public static void ConvertColumnType<T>(this DataTable dt, string columnName, TnewType) where T : Type, IConvertible
TM
3
Ich denke, dies ist die eleganteste Lösung. Vermeiden Sie es einfach, dr[dc.ColumnName] = dr[columnName] == DBNull.Value ? DBNull.Value : Convert.ChangeType(dr[columnName], newType);
DBNulls zu
8

Ändern Sie auch den Rückgabetyp:

select cast(columnName as int) columnName from table
tsilb
quelle
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)
Superna Parajuli
quelle
8

Ich habe einen etwas anderen Ansatz gewählt. Ich musste eine Datums- / Uhrzeitangabe aus einem Excel-Import im OA-Datumsformat analysieren. Diese Methode ist einfach genug, um aus ... im Wesentlichen

  1. Fügen Sie eine Spalte des gewünschten Typs hinzu
  2. Durchlaufen Sie die Zeilen, um den Wert zu konvertieren
  3. Löschen Sie die ursprüngliche Spalte und benennen Sie sie in die neue um, um sie an die alte anzupassen

    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;
        }
John Salewski
quelle
Vielen Dank! Genau das, wonach ich gesucht habe.
Rahul Singh
4

Sobald a DataTableausgefüllt wurde, können Sie den Typ einer Spalte nicht mehr ändern.

In diesem Szenario können Sie am besten eine Int32Spalte hinzufügen , DataTablebevor Sie sie ausfüllen:

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

Anschließend können Sie die Daten aus Ihrer ursprünglichen Tabelle in die neue Tabelle klonen:

DataTable dataTableClone = dataTable.Clone();

Hier ist ein Beitrag mit mehr Details .

Josh Earl
quelle
4

Wenn Sie nur eine Spalte ändern möchten, können Sie beispielsweise die Expression-Eigenschaft verwenden:

DataColumn col = new DataColumn("col_int" , typeof(int));
table.Columns.Add(col);
col.Expression = "table_exist_col_string"; // digit string convert to int  
mehdi
quelle
gute Antwort! Keine Notwendigkeit, (...) zu suchen und Nullwerte zu überprüfen.
Behzad Ebrahimi
2

Ich habe eine Erweiterungsfunktion erstellt, mit der der Spaltentyp einer DataTable geändert werden kann. Anstatt die gesamte Tabelle zu klonen und alle Daten zu importieren, wird nur die Spalte geklont, der Wert analysiert und dann das Original gelöscht.

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

Sie können es so verwenden:

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

Der obige Code ändert alle Dezimalspalten in Doppel.

Wes Hanney
quelle
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" );
Altivo
quelle
1

Ich habe die Effizienz von Marks Lösung - damit ich nicht .Clonedie gesamte DataTable verwenden muss - mit Generika und Erweiterbarkeit kombiniert , damit ich meine eigene Konvertierungsfunktion definieren kann . Das ist, was ich am Ende hatte:

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

Auf diese Weise ist die Verwendung viel einfacher und gleichzeitig anpassbar:

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());
Marcell Toth
quelle
0

Puedes stimmt mit einer Kolumna überein, die sich mit der Frage befasst, wie man die Kolumna anterior eliminiert

TB.Columns.Add("columna1", GetType(Integer))    
TB.Select("id=id").ToList().ForEach(Sub(row) row("columna1") = row("columna2"))    
TB.Columns.Remove("columna2")
Jhonny Espinoza
quelle
1
Sie sollten Ihrer Antwort einen Kontext hinzufügen. Nur Code-Antworten haben keinen Wert, da sie als minderwertig angesehen werden.
Nawed Nabi Zada