sql >> Base de Datos >  >> RDS >> Mysql

Estilo Flatten Table Pivot para una vista de cuadrícula de datos

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.

  1. Obtenga el conjunto de datos maestro para trabajar
  2. Obtenga una lista de nombres únicos de estudiantes
  3. Los nombres de columna a usar provienen de una consulta de linq para extraer las fechas de clase únicas en la tabla de datos
  4. Crear una nueva DataTable por los resultados.
    • Después del StudentName y ClassCode un bucle agrega una columna para cada fecha que cualquiera la clase se encuentra. Los nombres de columna/texto de encabezado provienen de ColNames lista/matriz recién creada.

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.

  1. Recorra todos los estudiantes en la lista de estudiantes
  2. Para cada uno, extraiga una lista de todas las clases a las que asistieron del conjunto de datos maestros
  3. Recorra esas clases
  4. Extraiga las filas de la clase actual del conjunto de datos Student-Class
  5. Crear un nuevo DataRow usando las variables de iteración Student y Class para las primeras 2 columnas.
  6. 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 )
  7. 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.