Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Flattern von untergeordneten/übergeordneten Daten mit unbekannter Anzahl von Spalten

Nachdem ich viele Tage daran gearbeitet habe, habe ich es geschafft, dieses Problem selbst zu lösen. Was ich getan habe, war Folgendes;

In meiner ursprünglichen untergeordneten Klasse erstellen der MemberCode und der ElementCode dann einen eindeutigen Schlüssel, der im Wesentlichen den Spaltennamen angibt. Also ging ich noch einen Schritt weiter und fügte einen "Column_Name" hinzu, sodass ich

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

Dies spiegelte sich offensichtlich auch in meiner Datenbanktabelle wider.

Mein SQL zum Extrahieren der Daten sah dann so aus;

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

Die erste Sortierung nach der UniqueID ist entscheidend, um sicherzustellen, dass die Datensätze für die spätere Verarbeitung in der richtigen Reihenfolge sind.

Ich würde einen dynamic verwenden und ein ExpandoObject() um die Daten zu speichern.

Also iteriere ich über das Ergebnis, um das SQL-Ergebnis wie folgt in diese Struktur umzuwandeln:

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

Dann habe ich meine Informationen dynamisch in der von mir gewünschten flachen Weise gespeichert.

Das nächste Problem war nun die ObjectListView, die ich verwenden wollte. Dies konnte solche dynamischen Typen nicht akzeptieren.

Ich hatte also die Informationen wie gewünscht in meinem Code gespeichert, konnte sie aber immer noch nicht wie erforderlich anzeigen.

Die Lösung bestand darin, eine Variante des ObjectListView zu verwenden bekannt als DataListView . Dies ist praktisch das gleiche Steuerelement, kann aber datengebunden sein. Eine andere Alternative wäre auch die Verwendung einer DatagridView, aber ich wollte aus anderen Gründen bei der ObjectListView bleiben.

Also musste ich jetzt meine dynamischen Daten in eine Datenquelle konvertieren. Dies habe ich wie folgt gemacht;

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

Dann weise ich einfach meine Datenquelle der DataListView zu, generiere die Spalten, und schwupps habe ich jetzt meine dynamischen Daten extrahiert, reduziert und wie gewünscht angezeigt.