Mysql
 sql >> Base de données >  >> RDS >> Mysql

Aplatir les données enfant/parent avec un nombre inconnu de colonnes

Après de nombreux jours de travail sur ce problème, j'ai réussi à résoudre ce problème moi-même. Ce que j'ai fait était le suivant ;

Dans ma classe enfant d'origine, MemberCode et ElementCode forment une clé unique qui indique essentiellement le nom de la colonne. Je suis donc allé un peu plus loin et j'ai ajouté un "Column_Name" pour que j'aie

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

Cela se reflétait évidemment également dans ma table de base de données.

Mon SQL pour extraire les données ressemblait alors à ceci ;

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

le classement par UniqueID est essentiel pour s'assurer que les enregistrements sont dans le bon ordre pour un traitement ultérieur.

Le j'utiliserais un dynamic et un ExpandoObject() pour stocker les données.

J'itère donc sur le résultat pour convertir le résultat sql dans cette structure comme suit ;

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

Ensuite, mes informations sont stockées dynamiquement de la manière plate dont j'ai besoin.

Maintenant, le problème suivant était l'ObjectListView que je voulais utiliser. Cela ne pouvait pas accepter de tels types dynamiques.

J'avais donc les informations stockées dans mon code comme je le voulais, mais je ne pouvais toujours pas les afficher comme il le fallait.

La solution était d'utiliser une variante de ObjectListView connu sous le nom de DataListView . Il s'agit en fait du même contrôle mais peut être lié aux données. Une autre alternative serait également d'utiliser un DatagridView, mais je voulais m'en tenir à ObjectListView pour d'autres raisons.

Alors maintenant, je devais convertir mes données dynamiques en une source de données. Ce que j'ai fait comme suit ;

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

Ensuite, j'assigne simplement ma source de données au DataListView, génère les colonnes, et hop, mes données dynamiques sont maintenant extraites, aplaties et affichées comme je le souhaite.