domingo, 7 de febrero de 2010

C# – DataTable – Pasar Filas a Columnas y agregar filas adicionales

 

Introducción

Basado en una pregunta que he visto en uno de los foros arme unos ejemplo que explica como poder trabajar con DataTables.

Básicamente el planteo de las consultas se hacían porque era necesario realizar tareas que requieren de la iteración de datos existentes, para ser volcados en un nuevo conjunto de datos resultante.

se atacara dos aspectos en este articulo

  • como agregar una línea en blanco, según cierto corte de control
  • como unir dos conjuntos de datos, pero que estas sean representadas en columnas
 

Línea en Blanco

Como se parecía en el código es necesario ciclar cada ítem de los datos obtenidos y mediante un procesamiento o corte de control en cuyo ejemplo es representado mediante un asiento contable, el cual solo es a modo de ejemplo.

 

private void Form2_Load(object sender, EventArgs e)
{
    //se obtiene el conjunto de datos
    DataTable dtAsientos = GetDatos();

    //se crea el dataset resultado 
    DataTable dt = new DataTable();

    dt.Columns.Add("Asiento");
    dt.Columns.Add("Importe");

    // variables que actuaran como flag para el corte de control
    // en el ciclo
    string asientActual = "";
    double importe = 0; 


    foreach (DataRow row in dtAsientos.Rows)
    {

        if (string.IsNullOrEmpty(asientActual))
        {
            // la primera vez que entre esta sin inicializar la variable "asientActual"
            // por eso entrara por este pregunta del if

            asientActual = Convert.ToString(row["asiento"]);
            importe = Convert.ToDouble(row["Importe"]);
        }
        else if (Convert.ToString(row["asiento"]) == asientActual)
        {
            //si el asiento es el mismo solo acumulo el importe
            importe += Convert.ToDouble(row["Importe"]);
        }
        else
        {

            //aqui es donde se agregas la nueva fila
            DataRow blankRow = dt.NewRow();

            blankRow["Asiento"] = "";
            blankRow["Importe"] = importe.ToString("N2");
            dt.Rows.Add(blankRow);

            //se inicializa las variable al nuevo asiento
            asientActual = Convert.ToString(row["asiento"]);
            importe = Convert.ToDouble(row["Importe"]);
        }

        DataRow newRow = dt.NewRow();

        newRow["Asiento"] = row["Asiento"];
        newRow["Importe"] = row["Importe"];

        dt.Rows.Add(newRow);

    }

    // se agrega el registro que refleja el acumulado 
    // para el ultimo asiento, esto se hace fuera del foreach
    // ya que no se poden registro adicionales que agreguen el ultimo valor
    DataRow lastRow = dt.NewRow();

    lastRow["Asiento"] = "";
    lastRow["Importe"] = importe.ToString("N2");
    dt.Rows.Add(lastRow);

    //se carga el nuevo dataset en la grilla
    dataGridView1.AutoGenerateColumns = true;
    dataGridView1.DataSource = dt; 

}

En el código además se lleva un acumulado de los importes de cada registro, y justo en el momento de insertar la línea adicional es que se usa este importe para informar el total del asiento.

 

Pasar Filas a Columnas, con dos Datatable

en este ejemplo se observaran dos orígenes de datos, lo cuales serán procesador para obtener un único resultado final, pero en donde la unión no sea a nivel de registros, sino que se agregaran las columnas de uno a continuación de las otras.

 

private void Form1_Load(object sender, EventArgs e)
        {
            //
            // obtengo los datos que seran unidos postermente
            //
            DataTable dt1 = GetDatos();
            DataTable dt2 = GetDatos2();

            //
            // se crea un nuevo datatable resultande de la union
            //
            DataTable dt = new DataTable();

            dt.Columns.Add("Columna1");
            dt.Columns.Add("Columna2");
            dt.Columns.Add("Columna3");
            dt.Columns.Add("Columna4");
            dt.Columns.Add("Columna5");


            //
            // se recorre el primer origen de datos
            // en este se insertan los registros 
            //
            foreach (DataRow row in dt1.Rows)
            {
                DataRow newRow = dt.NewRow();

                newRow["Columna1"] = row["Columna1"];
                newRow["Columna2"] = row["Columna2"];

                dt.Rows.Add(newRow);  

            }

            //se recorre el segundo origen de datos
            for (int i = 0; i < dt2.Rows.Count; i++)
            {
                DataRow copyRow = null;

                // se pregunta si el indice del registro existe
                // o debe crearse uno adicional, ya que el segundo origen de datos
                // posee mas registro que el primero
                if (dt.Rows.Count <= i)
                {
                    copyRow = dt.NewRow();
                    dt.Rows.Add(copyRow);
                }
                else
                    copyRow = dt.Rows[i];

                DataRow originalRow = dt2.Rows[i];

                copyRow["Columna3"] = originalRow["Columna3"];
                copyRow["Columna4"] = originalRow["Columna4"];
                copyRow["Columna5"] = originalRow["Columna5"];
            }


            dataGridView1.AutoGenerateColumns = true; 
            dataGridView1.DataSource = dt; 
            
        }

 

En el código se observar que el dataset de resultado al crearlo se le definen las columnas de los dos dataset que son usados de origen de datos.

El primer ciclo por contar con el dataset resultado sin valores simplemente vuelva los registros allí.

El segundo es algo mas complejo ya que debe usas los índices de los registros creados en el paso anterior.

Además debe verificar si es que este segundo grupo de registro es mayor que el anterior de agregar las filas adicionales.

Conclusión

Para trabajar con DataTables para manipular la información que estos contienen y poder forma un conjunto de datos adicional que represente correctamente la datos en la disposición que se necesita, es necesario mencionar que un punto a tener en cuenta y esta referido al orden en se que proveen los datos iniciales.

Como se verán en ambos ejemplo el orden es importante ya que este determina el éxito del corte que se aplica al recorrer y procesar cada registro.

[C#]
 

10 comentarios:

  1. Hola Leandro no puedo descargar el codigo del ejemplo parece que ya no esta disponible.

    ResponderEliminar
  2. hola Augusto

    Por problemas con skydrive los links de los articulos dejaron de ser validos.

    Pero ya actualice para que puedas descargarlo.

    saludos

    ResponderEliminar
  3. Hola Leandro, te agradecería mucho si me ayudaras con un ejemplo parecido en C# pero en webforms. Tengo una consulta que hago desde C# a SAP y la guardo en un datatable, pero esa bapi me retorna una sola columna con n cantidad de filas y necesito separar esas filas en otras columnas y en un datagrid. Gracias y espero tu pronta respuesta

    ResponderEliminar
  4. hola adridelvaller

    lo que podrias hacer es aplcia la misma tecnica, create un datatable auxiliar en dodne definas las columnas que necesitas

    luego recorres las filas del datatable original y vas tomando la info asignando a la DataRow del temporal, para al final cuando termines de recorrer asignas el datarow que se genera el dt temporal con todas las columnas

    saludos

    ResponderEliminar
  5. Hola Leandro que tal, tengo un problema, quiero hacer todo el proceso de agregar automáticamente columnas a una tabla en SQL SERVER. espero y me entiendan, quiero agregar automáticamente varias columnas a una tabla (ALUMNO_ACT) con los registros de otra tabla(mat_prepa), es decir quiero recorrer cada uno de los registros de la tabla “mat_prepa” y poner la condición de que si no existe una columna con ese nombre en la tabla “ALUMNO_ACT” que cree una columna con el registro captado en el cursor y a si sucesivamente hasta terminar con todos los registros de la tabla “mat_prepa”. dejo el código:
    DECLARE @resp varchar(20)
    DECLARE @cursor cursor
    SET @cursor = CURSOR FOR
    SELECT cve_mat FROM mat_prepa

    OPEN @cursor
    FETCH NEXT FROM @cursor
    WHILE @@FETCH_STATUS = 0
    BEGIN
    FETCH NEXT FROM @cursor
    IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ‘ALUMNO_ACT’ AND COLUMN_NAME = @resp)
    ALTER TABLE ALUMNO_ACT
    ADD [@cursor] varchar (20)
    END

    –El problema esta en: ADD [@cursor] varchar (20).

    ni declarando una variable y asignándole el valor de @cursor me deja, existe alguna otra forma?,

    existe alguna forma de hacerlo desde visual studio c#?

    Saludos!

    ResponderEliminar
  6. Hola Leandro gusto saludarte, como puedo persistir un datatable en un asp.net

    ResponderEliminar
  7. hola richard10

    lo harias de la misma forma que con cualquier otra aplicacion
    solo que necesitas conocer sobre ado.net

    sabes como realizar un INSERT con ado.net ? que base de datos estas utilizando?

    o podrias usar el SqlDataAdapter, usando el metodo Update(), pero eso depende como estes actualizando el dataset, yo recomendaria tomes los valroes de forma simple de los controles y definas un INSERT con parametros para ejecutarlo con el ExecuteNonQuery()

    saludos

    ResponderEliminar
  8. Saludos Leandro, tengo la sig problemática:
    Tengo dos DataTable, una se llama dt y la otra dtINV, al momento de hacer una consulta por separado en cada una me arroja dos registros, es decir 2 registros la dt y dos registro la dtINV, lo que deseo hacer es convinar esas dos consultas y que solo me arroje dos registros.
    Gracias por la ayuda

    ResponderEliminar
  9. hola Reynaldo

    no te animas a usar linq sobre estos datatable para poder relacionarlos con un join

    [Linq] Join DataTable

    con esto podrias relacionar por un campo los datos

    saludos

    ResponderEliminar
  10. Saludos Leandro, gracias por contestar, al final lo he solucionado, ah quedado de la sig. manera:

    DataTable table = new DataTable("Union");
    //crear las nuevas columnas
    DataColumn[] newcolumns = new DataColumn[dt.Columns.Count];
    DataColumn[] newcolumns2 = new DataColumn[dtINV.Columns.Count];
    for (int i = 0; i < dt.Columns.Count; i++)
    {
    newcolumns[i] = new DataColumn(dt.Columns[i].ColumnName, dt.Columns[i].DataType);
    }
    for (int i = 0; i < dtINV.Columns.Count; i++)
    {
    newcolumns2[i] = new DataColumn(dtINV.Columns[i].ColumnName, dtINV.Columns[i].DataType);
    }

    //agregar las columnas vacias (esquema o estructura) a la tabla resultante
    table.Columns.AddRange(newcolumns);
    table.Columns.AddRange(newcolumns2);
    table.BeginLoadData();
    //cargar los datos desde la primera tabla
    foreach (DataRow roww in dt.Rows)
    {
    table.LoadDataRow(roww.ItemArray, true);
    }
    //cargar los datos desde la segunda tabla
    for (int re = 0; re < dtINV.Rows.Count; re++)
    {
    for (int c = 0; c < dtINV.Columns.Count; c++)
    {
    string cn = dtINV.Columns[c].ColumnName.ToString();
    table.Rows[re][cn] = dtINV.Rows[re][cn];
    }
    }
    table.EndLoadData();

    ResponderEliminar