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

Consulta PHP e Mysql, use PHP para converter linha em colunas


É possível obter o resultado usando uma consulta SQL, mas não é trivial.

Mas antes de seguir esse caminho, recomendo que você considere uma abordagem diferente.

Como a consulta está retornando um conjunto relativamente pequeno de linhas, você pode recuperar todo o conjunto de resultados no PHP como um array bidimensional.

Considere um caso bastante simples, como ilustração:
SELECT foo, fee, fi, fo, fum
  FROM mytable 
 ORDER BY foo

foo fee fi  fo  fum
--- --- --- --- ---
ABC   2   3   5   7
DEF  11  13  17  19

Poderíamos fazer um fetchAll e obter um array bidimensional, depois percorrer o array e recuperar os valores em coluna, em vez de linha. Uma opção é transformar o array que recebemos em um novo array parecido com este:
bar  ABC  DEF
---  ---  ---
fee    2   11
fi     3   13
fo     5   17
fum    7   19

Não é realmente necessário fazer a transformação, você pode andar no array original. Mas separar a transformação como uma etapa separada provavelmente tornaria seu código um pouco mais fácil, quando você realmente gerar a saída na página. (Parece um problema bastante comum que alguém provavelmente escreveu uma função que faz a transformação de array que você deseja. Eu não acho que exista um PHP embutido que faça isso.

títulos:
array { [0]=>'bar'  [1]=>'ABC'  [2]=>'DEF' }

linhas:
array {
  [0]=>array { [0]=>'fee'   [1]=>'2'  [2]=>'11' }
  [1]=>array { [0]=>'fi'    [1]=>'3'  [2]=>'13' }
  [2]=>array { [0]=>'fo'    [1]=>'5'  [2]=>'17' }
  [3]=>array { [0]=>'fum'   [1]=>'7'  [2]=>'19' }
}

Para um pequeno conjunto de linhas como você tem, eu optaria por fazer isso no PHP em vez de no SQL.

Mas você perguntou como fazer isso no SQL. Como eu disse anteriormente, não é trivial.

SQL requer que a instrução SELECT defina cada coluna a ser retornada; o número e os tipos das colunas não podem ser dinâmicos quando a instrução é executada.

Se construirmos outra consulta (além da consulta original) que defina as colunas e retorne as linhas que esperamos que sejam retornadas com espaços reservados para os valores, estaremos na metade do caminho. Tudo o que resta é fazer uma junção externa às linhas retornadas pela consulta original e retornar condicionalmente os valores das colunas nas linhas apropriadas.

Essa abordagem funciona se você tiver um conjunto predefinido de linhas e colunas que precisamos retornar, especialmente quando a origem da linha original for esparsa e precisarmos gerar as linhas "ausentes". (Por exemplo, ao obter contagens de produtos pedidos e há muitas linhas ausentes, não há uma boa maneira de gerar as linhas ausentes.

Por exemplo:
SELECT r.bar
     , '' AS `ABC`
     , '' AS `DEF`
  FROM ( SELECT 'fee' AS bar
          UNION ALL SELECT 'fi'
          UNION ALL SELECT 'fo'
          UNION ALL SELECT 'fum'
       ) r
 GROUP BY r.bar

Isso retornará:
 bar  ABC  DEF
 ---  ---  ---
 fee
 fi
 fo
 fum

Então, isso nos dá todas as colunas definidas e todas as linhas que queremos retornar. A primeira coluna é preenchida. Essa consulta ainda não precisa do GROUP BY, mas vamos precisar dele quando correspondermos às linhas do conjunto de resultados de origem "real".

O "truque" agora é combinar as linhas de nossa fonte e retornar o valor de uma coluna com base nas condições apropriadas.

O que vamos gerar, essencialmente, é um conjunto de resultados que se parece com isso:
bar  foo  ABC  DEF
---  ---  ---  ---
fee  ABC    2
fee  DEF        11
fi   ABC    3
fi   DEF        13
fo   ABC    5
fo   DEF        15
fum  ABC    7
fum  DEF        17

Então vamos "recolher" as linhas, removendo a coluna foo do conjunto de resultados e fazendo um GROUP BY na bar . Vamos usar uma função agregada (seja MAX ou SUM) aproveitando o tratamento que eles fazem com valores NULL, para produzir um resultado como este:
bar  foo  ABC  DEF
---  ---  ---  ---
fee         2   11
fi          3   13
fo          5   15
fum         7   17

Usando este SQL bastante complicado:
SELECT r.bar
     , MAX(CASE WHEN t.foo = 'ABC' THEN CASE r.bar 
         WHEN 'fee' THEN t.fee 
         WHEN 'fi'  THEN t.fi
         WHEN 'fo'  THEN t.fo
         WHEN 'fum' THEN t.fum
       END END) AS 'ABC'
     , MAX(CASE WHEN t.foo = 'DEF' THEN CASE r.bar 
         WHEN 'fee' THEN t.fee 
         WHEN 'fi'  THEN t.fi
         WHEN 'fo'  THEN t.fo
         WHEN 'fum' THEN t.fum
       END END) AS 'DEF'
  FROM ( SELECT 'foo' AS col
          UNION ALL SELECT 'fee'
          UNION ALL SELECT 'fi'
          UNION ALL SELECT 'fo'
          UNION ALL SELECT 'fum'
       ) r
 CROSS
  JOIN mysource t
 GROUP BY r.bar

Observe que mysource na consulta acima pode ser substituído por uma visualização inline, envolvendo parênteses em torno de uma consulta adequada que retorna as linhas que desejamos.

A visualização em linha alias como r é nossa fonte para retornar as linhas que queremos retornar.

As expressões na lista SELECT estão fazendo os testes condicionais, para escolher os valores corretos para cada coluna em cada linha.

Dado o padrão regular das instruções CASE, é possível usar algum SQL para ajudar a gerar a consulta, mas isso deve ser feito em uma etapa separada. A saída desse SQL pode ser usada para ajudar a formar a consulta real de que precisamos.

No seu caso, dado que workdate é o que você deseja usar para o cabeçalho da coluna, isso provavelmente terá que ser gerado dinamicamente. (Você pode remover essa segunda coluna 'dia da semana' da consulta de origem original e movê-la para a consulta externa.

Se eu não souber a workdate valores para os títulos antes de executar a consulta, então optaria por criar uma TABELA TEMPORÁRIA e preenchê-la com os resultados da consulta original e, em seguida, consultar a tabela temporária para obter o workdate cabeçalhos e a "primeira coluna" para gerar as linhas. Então eu executaria a consulta real na tabela temporária.

Para repetir, acho que seria melhor fazer a transformação/pivot dos resultados de sua consulta original em PHP, em vez de tentar fazê-lo em SQL.