sql >> Base de Datos >  >> RDS >> Mysql

Flattern child/parent data con un número desconocido de columnas

Después de muchos días trabajando en esto, he logrado resolver este problema yo mismo. Lo que hice fue lo siguiente;

En mi clase secundaria original, MemberCode y ElementCode crean una clave única que básicamente indica cuál era el nombre de la columna. Así que llevé esto un paso más allá y agregué un "Nombre_Columna" para tener

public class Child
{
        public int MemberCode { get; set; }   //Composite key
        public int ElementCode { get; set; }  //Composite key
        public string Column_Name { get; set; }  //Unique.  Alternative Key
        public Object Data { get; set; }
}

Obviamente, esto también se reflejó en la tabla de mi base de datos.

Mi SQL para extraer los datos se veía así;

select  p.UniqueID, p.LoadTime, p.reference, c.MemberCode, c.ElementCode , c.column_name, c.Data 
from parent as p, child as c
where p.UniqueID = c.UniqueID 
//aditional filter criteria
ORDER BY p.UniqueID, MemberCode, ElementCode

ordenar primero por UniqueID es fundamental para garantizar que los registros estén en el orden correcto para su posterior procesamiento.

Yo usaría un dynamic y un ExpandoObject() para almacenar los datos.

Así que itero sobre el resultado para convertir el resultado de sql en esta estructura de la siguiente manera:

List<dynamic> allRecords = new List<dynamic>();  //A list of all my records
List<dynamic> singleRecord = null;  //A list representing just a single record

bool first = true;   //Needed for execution of the first iteration only
int lastId = 0;      //id of the last unique record

foreach (DataRow row in args.GetDataSet.Tables[0].Rows)
{
    int newID = Convert.ToInt32(row["UniqueID"]);  //get the current record unique id   

    if (newID != lastId)  //If new record then get header/parent information
    {
        if (!first)
            allRecords.Add(singleRecord);   //store the last record
        else
            first = false;

        //new object
        singleRecord = new List<dynamic>();

        //get parent information and store it
        dynamic head = new ExpandoObject();
        head.Column_name = "UniqueID";
        head.UDS_Data = row["UniqueID"].ToString();
        singleRecord.Add(head);

        head = new ExpandoObject();
        head.Column_name = "LoadTime";
        head.UDS_Data = row["LoadTime"].ToString();
        singleRecord.Add(head);

        head = new ExpandoObject();
        head.Column_name = "reference";
        head.UDS_Data = row["reference"].ToString();
        singleRecord.Add(head);                    
    }

    //get child information and store it.  One row at a time
    dynamic record = new ExpandoObject();

    record.Column_name = row["column_name"].ToString();
    record.UDS_Data = row["data"].ToString();
    singleRecord.Add(record);

    lastId = newID;   //store the last id
}
allRecords.Add(singleRecord);  //stores the last complete record

Entonces tengo mi información almacenada dinámicamente de la manera plana que requiero.

Ahora el siguiente problema era el ObjectListView que quería usar. Esto no podría aceptar tales tipos dinámicos.

Así que tenía la información almacenada dentro de mi código como quería, pero aún no podía mostrarla como se requería.

La solución fue usar una variante de ObjectListView conocido como DataListView . Este es efectivamente el mismo control, pero puede vincularse a datos. Otra alternativa también sería usar un DatagridView, pero quería ceñirme a ObjectListView por otras razones.

Así que ahora tenía que convertir mis datos dinámicos en una fuente de datos. Esto lo hice de la siguiente manera;

DataTable dt = new DataTable();            
foreach (dynamic record in allRecords)
{
    DataRow dr = dt.NewRow();
    foreach (dynamic item in record)
    {
        var prop = (IDictionary<String, Object>)item;
        if (!dt.Columns.Contains(prop["Column_name"].ToString()))
        {
            dt.Columns.Add(new DataColumn(prop["Column_name"].ToString()));
        }

        dr[prop["Column_name"].ToString()] = prop["UDS_Data"];
    }
    dt.Rows.Add(dr);
}

Luego, simplemente asigno mi fuente de datos a DataListView, genero las columnas y listo, ahora tengo mis datos dinámicos extraídos, aplanados y mostrados como los necesito.