Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Flatten Table Pivot style para um Datagridview


Dependendo do que você está fazendo, em alguns casos você pode criar uma consulta ou instrução preparada para fazer isso por você. Estes são geralmente para resumir um conjunto fixo de colunas conhecidas. Neste caso não sabemos quantas colunas de data existirão ou quais são. Então, podemos fazer isso em código.

Eu usei Access em vez de mySQL, mas o conceito é o mesmo. Eu também tornei isso mais complexo registrando a frequência por aula que não atende todos os dias. Os dados iniciais:



Eu não vou usar o nome da classe nos resultados, isso torna a exibição muito ampla.
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

Não é tão complexo quanto pode parecer.
  1. Obtenha o conjunto de dados mestre para trabalhar
  2. Obtenha uma lista de nomes exclusivos de alunos
  3. Os nomes das colunas a serem usados ​​vêm de uma consulta linq para extrair as datas de classe exclusivas na tabela de dados
  4. Crie uma nova DataTable pelos resultados.
    • Depois do StudentName e ClassCode um loop adiciona uma coluna para cada data que qualquer classe atende. Os nomes das colunas/texto do cabeçalho vem do ColNames lista/matriz recém-criada.

Com a DataTable de destino criada, você pode começar a copiar dados para ela. Novamente, em vez de OleDB... objetos que você usaria MySQL... objeto, mas eles funcionam da mesma forma.
  1. Percorrer todos os alunos na lista de alunos
  2. Para cada um, extraia uma lista de todas as aulas que eles participaram do conjunto de dados mestre
  3. Passe por essas classes
  4. Extraia as linhas da turma atual do conjunto de dados Student-Class
  5. Crie um novo DataRow usando as variáveis ​​de iteração Student e Class para as duas primeiras colunas.
  6. Converta cada valor de DateTime no conjunto de dados atual de Student-Class para o mesmo formato usado para criar as colunas de resultado (cname ).
    • use-o para copiar seu status:newRow.Item(cname) = statRow.Item("Status") para a nova linha
    • Como as classes não se reúnem todos os dias, algumas células ficarão em branco (DbNull )
  7. Adicione a nova linha à tabela de dados final

Seria mais simples sem o relatório Por classe e apenas relatar o status do dia inteiro. O resultado:



A parte mais confusa é usar a Data dados em uma tabela de dados como uma coluna nome em outro e retirando a parte do tempo.

Essa é apenas uma primeira passagem, então provavelmente pode ser refinada. Alguns dos processamentos podem ser feitos em SQL; o DateTime.Parse método para converter o DateTime data para uma string no mesmo formato (remover o Time etc) poderia ser seu próprio procedimento. Eu também usaria um formato de ano de 2 caracteres para tornar os cabeçalhos um pouco mais estreitos.