Dependiendo de lo que esté haciendo, en algunos casos puede crear una consulta o declaración preparada para hacer esto por usted. Suelen ser para resumir un conjunto fijo de columnas conocidas. En este caso no sabemos cuántas columnas de fecha habrá ni cuáles son. Entonces, podemos hacerlo en código.
Usé Access en lugar de mySQL, pero el concepto es el mismo. También lo hice más complejo al registrar la asistencia por clase que no se reúne todos los días. Los datos iniciales:
No usaré el nombre de la clase en los resultados, hace que la visualización sea demasiado amplia.
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
No es tan complejo como puede parecer.
- Obtenga el conjunto de datos maestro para trabajar
- Obtenga una lista de nombres únicos de estudiantes
- Los nombres de columna a usar provienen de una consulta de linq para extraer las fechas de clase únicas en la tabla de datos
- Crear una nueva
DataTable
por los resultados.- Después del
StudentName
yClassCode
un bucle agrega una columna para cada fecha que cualquiera la clase se encuentra. Los nombres de columna/texto de encabezado provienen deColNames
lista/matriz recién creada.
- Después del
Con el DataTable de destino creado, puede comenzar a copiar datos en él. De nuevo, en lugar de OleDB...
objetos que usaría MySQL...
objeto, pero funcionan igual.
- Recorra todos los estudiantes en la lista de estudiantes
- Para cada uno, extraiga una lista de todas las clases a las que asistieron del conjunto de datos maestros
- Recorra esas clases
- Extraiga las filas de la clase actual del conjunto de datos Student-Class
- Crear un nuevo
DataRow
usando las variables de iteración Student y Class para las primeras 2 columnas. - Convierta cada valor de fecha y hora en el conjunto de datos de clase de estudiante actual al mismo formato utilizado para crear las columnas de resultados (
cname
).- utilícelo para copiar su estado:
newRow.Item(cname) = statRow.Item("Status")
a la nueva fila - Dado que las clases no se reúnen todos los días, algunas celdas estarán en blanco (
DbNull
)
- utilícelo para copiar su estado:
- Agregue la nueva fila a la tabla de datos final
Sería más sencillo sin el informe Por clase y solo informar el estado de todo el día. El resultado:
La parte más confusa es usar la Fecha datos en una tabla de datos como una columna nombre en otro y quitando la porción de tiempo.
Eso es solo un primer paso, por lo que es probable que se pueda refinar. Parte del procesamiento podría realizarse en SQL; el DateTime.Parse
método para convertir el DateTime
datos a una cadena en el mismo formato (quitar el tiempo, etc.) podría ser su propio procedimiento. También usaría un formato de año de 2 caracteres para que los encabezados sean un poco más angostos.