A seconda di ciò che stai facendo, in alcuni casi puoi creare una query o una dichiarazione preparata per farlo per te. Di solito servono a riassumere un insieme fisso di colonne note. In questo caso non sappiamo quante colonne di data ci saranno o quali siano. Quindi, possiamo farlo in codice.
Ho usato Access invece di MySQL, ma il concetto è lo stesso. L'ho anche reso più complesso registrando le presenze per classe che non si incontrano tutti i giorni. I dati di partenza:
Non userò il nome della classe nei risultati, rende il display troppo ampio.
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
Non è così complesso come potrebbe sembrare.
- Ottieni il set di dati principale su cui lavorare
- Ottieni un elenco di nomi di studenti univoci
- I nomi delle colonne da utilizzare provengono da una query linq per estrarre le date delle classi univoche nella tabella dei dati
- Crea una nuova
DataTable
per i risultati.- Dopo il
StudentName
eClassCode
un ciclo aggiunge una colonna per ogni data che qualsiasi la classe si incontra. I nomi delle colonne/il testo dell'intestazione deriva daColNames
elenco/array appena creato.
- Dopo il
Con il DataTable di destinazione creato, puoi iniziare a copiare i dati su di esso. Di nuovo, invece di OleDB...
oggetti che useresti MySQL...
oggetto, ma funzionano allo stesso modo.
- Scorri tutti gli studenti nell'elenco degli studenti
- Per ciascuno, estrai un elenco di tutte le lezioni a cui hanno partecipato dal set di dati principali
- Scorri queste classi
- Estrai le righe per la classe corrente dal set di dati Student-Class
- Crea un nuovo
DataRow
utilizzando le variabili di iterazione Studente e Classe per le prime 2 colonne. - Converti ogni valore DateTime nell'attuale dataset Student-Class nello stesso formato utilizzato per creare le colonne dei risultati (
cname
).- usalo per copiare il loro stato:
newRow.Item(cname) = statRow.Item("Status")
alla nuova riga - Dato che le classi non si incontrano tutti i giorni, alcune celle saranno vuote (
DbNull
)
- usalo per copiare il loro stato:
- Aggiungi la nuova riga al datatable finale
Sarebbe più semplice senza il report Per classe e riportare solo lo stato per l'intera giornata. Il risultato:
La parte più confusa è l'utilizzo dei dati della data in un datatable come colonna nome in un altro ed eliminando la parte del tempo.
Questo è solo un primo passaggio, quindi può probabilmente essere perfezionato. Alcune delle elaborazioni potrebbero essere eseguite in SQL; il DateTime.Parse
metodo per convertire il DateTime
dati in una stringa nello stesso formato (rimuovere l'ora ecc.) potrebbe essere la propria procedura. Userei anche un formato anno a 2 caratteri per rendere le intestazioni un po' più strette.