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

Flatten Table Pivot-Stil für eine Datagridview

Je nachdem, was Sie tun, können Sie in einigen Fällen eine Abfrage oder eine vorbereitete Anweisung erstellen, um dies für Sie zu tun. Diese dienen normalerweise dazu, einen festen Satz bekannter Spalten zusammenzufassen. In diesem Fall wissen wir nicht, wie viele Datumsspalten es geben wird oder was sie sind. Also können wir es im Code machen.

Ich habe Access anstelle von mySQL verwendet, aber das Konzept ist dasselbe. Ich habe es auch komplexer gemacht, indem ich die Anwesenheit nach Klassen protokolliert habe, die sich nicht jeden Tag treffen. Die Startdaten:

Ich werde den Klassennamen nicht in den Ergebnissen verwenden, das macht die Anzeige zu breit.

Dim sql = <sql>  
           ((Use your own SQL obviously))
           </sql>.Value

Dim dtTemp As New DataTable

' get the data
Using dbcon As OleDbConnection = GetACEConnection(),
    cmd As New OleDbCommand(sql, dbcon)

    dbcon.Open()
    Using da As New OleDbDataAdapter(cmd)
        da.Fill(dtTemp)
    End Using

End Using

' unique list of "date" columns in the result set
' ORDERBY Date is in the SQL
Dim colNames = dtTemp.AsEnumerable().
                Select(Function(s) DateTime.Parse(s.Item("Date").ToString).
                        ToString("MM/dd/yyyy")).
                Distinct.ToList()

' unique list of students
Dim Students = dtTemp.AsEnumerable().Select(Function(q) q.Item("Name")).
                Distinct.ToList()

' the final table to use with the DGV
Dim dt As New DataTable
Dim colName As String

' add the name and class code designation columns
dt.Columns.Add(New DataColumn(dtTemp.Columns(0).ColumnName, GetType(String)))
dt.Columns.Add(New DataColumn(dtTemp.Columns(1).ColumnName, GetType(String)))

' add a "MM/dd/yyyy" text column for each possible class day
For n As Int32 = 0 To colNames.ToArray.Count - 1
    colName = DateTime.Parse(colNames(n).ToString).ToString("MM/dd/yyyy")
    dt.Columns.Add(New DataColumn(colName, GetType(String)))
Next

Dim newRow As DataRow

' loop thru all students
For Each s In Students
    ' the student-class dataset
    Dim drs As DataRow() = dtTemp.Select(String.Format("Name = '{0}'", s.ToString)).
                            OrderBy(Function(o) o.Item("ClassCode")).ToArray

    ' create list of classes for this student
    Dim classes = drs.AsEnumerable.
            Select(Function(q) q.Item(1).ToString).Distinct.ToArray

    For Each classcode As String In classes
        ' filter the drs results to the current class
        Dim datestat As DataRow() = drs.AsEnumerable.
                Where(Function(q) q.Item(1).ToString = classcode).ToArray

        ' create new row, copy the data from drs.Rows to dt.columns
        newRow = dt.NewRow
        newRow.Item(0) = s
        newRow.Item(1) = classcode
        ' NOTE since not all students will have a class everyday, some
        ' "status" cells will be dbNull!
        For Each statRow In datestat
            Dim cname As String = DateTime.Parse(statRow.Item("Date").
                                                     ToString()).ToString("MM/dd/yyyy")
            newRow.Item(cname) = statRow.Item("Status")
        Next
        dt.Rows.Add(newRow)
    Next

Next

dgv.AutoGenerateColumns = True
dgv.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.ColumnHeader)
dgv.DataSource = dt

Es ist nicht ganz so komplex, wie es aussehen mag.

  1. Besorgen Sie sich das Master-Dataset zum Arbeiten
  2. Erhalten Sie eine Liste eindeutiger Schülernamen
  3. Die zu verwendenden Spaltennamen stammen aus einer linq-Abfrage, um die eindeutigen Klassendaten in der Datentabelle zu extrahieren
  4. Erstellen Sie eine neue DataTable für die Ergebnisse.
    • Nach dem StudentName und ClassCode Eine Schleife fügt eine Spalte für jedes Datum hinzu, das beliebig ist Klasse trifft. Die Spaltennamen/Überschriftentexte stammen aus ColNames gerade erstellte Liste/Array.

Nachdem die Ziel-DataTable erstellt wurde, können Sie mit dem Kopieren von Daten beginnen. Wieder statt OleDB... Objekte, die Sie MySQL... verwenden würden Objekt, aber sie funktionieren gleich.

  1. Durchlaufen Sie alle Schüler in der Schülerliste
  2. Extrahieren Sie für jeden eine Liste aller besuchten Kurse aus dem Stammdatensatz
  3. Durchlaufen Sie diese Klassen
  4. Extrahieren Sie die Zeilen für die aktuelle Klasse aus dem Student-Class-Datensatz
  5. Erstellen Sie eine neue DataRow Verwenden der Iterationsvariablen Student und Class für die ersten beiden Spalten.
  6. Konvertieren Sie jeden DateTime-Wert im aktuellen Student-Class-Datensatz in dasselbe Format, das zum Erstellen der Ergebnisspalten verwendet wurde (cname ).
    • verwenden Sie es, um ihren Status zu kopieren:newRow.Item(cname) = statRow.Item("Status") in die neue Zeile
    • Da sich die Klassen nicht jeden Tag treffen, sind einige Zellen leer (DbNull )
  7. Fügen Sie die neue Zeile zur endgültigen Datentabelle hinzu

Es wäre einfacher ohne die Berichterstattung nach Klasse und nur den Status für den ganzen Tag zu melden. Das Ergebnis:

Der verwirrendste Teil ist die Verwendung der Date Daten in einer Datentabelle als Spalte Name in einem anderen und Herausziehen des Zeitteils.

Das ist nur ein erster Durchgang, daher kann er wahrscheinlich verfeinert werden. Ein Teil der Verarbeitung kann möglicherweise in SQL erfolgen; die DateTime.Parse Methode zum Konvertieren von DateTime Daten in eine Zeichenfolge im gleichen Format (Entfernen der Zeit usw.) könnte eine eigene Prozedur sein. Ich würde auch ein 2-stelliges Jahresformat verwenden, um die Überschriften etwas schmaler zu machen.