MySQL adicionar total da coluna
-
20-08-2019 - |
Pergunta
Eu preciso consultar este DB para obter cada linha, mas também a soma de um dos valores da coluna dos resultados. Eu poderia usar php para obter o valor total, mas então eu preciso executar dois loops, um para obter o total (que vai no topo acima dos resultados). Então, eu prefiro a consulta para pegá-lo e fazer apenas uma linha "total", mas a única maneira que eu comecei-lo para o trabalho é com uma subconsulta que é essencialmente uma repetição da consulta original. Existe uma maneira melhor?
SELECT
CONCAT(u.firstname, ' ', u.lastname ) name, u.id, s.description, s.shiftstart, s.shiftend,
(SELECT
SUM( TIME_TO_SEC( TIMEDIFF( shiftend, shiftstart ) ) ) /3600
FROM shifts
WHERE id = '$user'
AND DATE( shiftstart )
BETWEEN '$start'
AND '$end') total
FROM shifts s
INNER JOIN users u ON ( s.id = u.id )
WHERE s.id = '$user'
AND DATE( shiftstart )
BETWEEN '$start'
AND '$end'
ORDER BY shiftstart
As obras e saídas acima:
name id description shiftstart shiftend total
Joe User joeuser Stuff 2009-01-05 07:45:00 2009-01-05 12:15:00 39.5000
Joe User joeuser Stuff 2009-01-05 13:00:00 2009-01-05 17:00:00 39.5000
Joe User joeuser Stuff 2009-01-06 07:45:00 2009-01-06 10:45:00 39.5000
Joe User joeuser Stuff 2009-01-06 10:45:00 2009-01-06 12:45:00 39.5000
Joe User joeuser Stuff 2009-01-06 13:30:00 2009-01-06 14:30:00 39.5000
Joe User joeuser Stuff 2009-01-06 14:30:00 2009-01-06 17:00:00 39.5000
Joe User joeuser Stuff 2009-01-07 09:45:00 2009-01-07 14:00:00 39.5000
Joe User joeuser Stuff 2009-01-07 15:00:00 2009-01-07 17:00:00 39.5000
Joe User joeuser Stuff 2009-01-08 08:00:00 2009-01-08 12:15:00 39.5000
Joe User joeuser Stuff 2009-01-08 13:15:00 2009-01-08 17:00:00 39.5000
Joe User joeuser Stuff 2009-01-09 07:45:00 2009-01-09 10:45:00 39.5000
Joe User joeuser Stuff 2009-01-09 11:45:00 2009-01-09 15:15:00 39.5000
Joe User joeuser Stuff 2009-01-09 15:15:00 2009-01-09 17:00:00 39.5000
O que é o que eu preciso, não, mas provavelmente a melhor maneira de obtê-lo.
Solução
O MySQL suporta um grupo-by modificador especial chamado ROLLUP .
SELECT CONCAT(u.firstname, ' ', u.lastname ) name, u.id,
s.description, s.shiftstart, s.shiftend,
SUM( TIME_TO_SEC( TIMEDIFF( shiftend, shiftstart ) ) ) /3600 total
FROM shifts s INNER JOIN users u ON ( s.id = u.id )
WHERE s.id = ? AND DATE( shiftstart ) BETWEEN ? AND ?
GROUP BY u.id, s.shiftstart WITH ROLLUP
ORDER BY shiftstart;
Outras dicas
A melhor maneira é fazer isso com o código. As pessoas continuam insistindo em usar SQL, que é uma álgebra relacional, para fazer deveres processuais. Tentando encaixar processual-ness para SQL é sempre uma má idéia, tanto em termos de complexidade e desempenho. Tome este conselho de um DBA profissional.
Run duas consultas do seu código. Saída do conjunto maior primeiro, em seguida, a linha total de em qualquer formato que você deseja. Suas perguntas serão menores e mais simples, o seu desempenho irá melhorar e você vai obter o resultado que você deseja.
Alguns outros conselhos - espaço em disco é barato e a maioria das tabelas de banco de dados são lidos muito mais frequentemente do que eles são escritos. Configurar um disparador de inserção / atualização (se possível no MySQL) para preencher uma coluna separada com campos calculados como "CONCAT(u.firstname,' ',u.lastname)
" e usar isso para consultas. Per-linha funções não são escaláveis ??e vai matar o seu desempenho DBMS, uma vez que se torna maior.
Qualquer coisa pode ser melhor em comparação; a questão é comparação com o que, né?
A questão-chave que você indicou é que você queria que o total no topo dos resultados; Crystal Reports e outros aplicativos, gerenciar este tipo de magia por ser um motor de 2 passagem. Primeira passagem recebe os totais.
Existem trade-offs com qualquer solução. Se você receber uma linha separada "total", em seguida, o aplicativo de recebimento terá que quer tira-lo dos resultados ou jogar algum outro truque para escondê-lo.
Uma possibilidade, que pode ser uma opção se você não está escrevendo para um site de 1 milhão de hit / h, é simplesmente fazer 2 chamadas - um para as informações gerais, tais como o nome, tempo total, etc, em seguida, para os detalhes ... parece a partir da consulta que você está selecionando os resultados de uma única pessoa.
Estamos todos para salvar a sobrecarga e largura de banda, mas às vezes, o simples é melhor ...
EDIT: Pax me bater para o botão "Salvar" ... lol ...
Use a extensão mysql fornecido exatamente para esta finalidade, conforme descrito na resposta de Bill Karwin (que eu upvoted eu).
Se isto não estavam disponíveis, a opção 2 seria outra instrução SQL: select sum ...) SQL realmente é otimizado para ser extremamente eficiente para esse tipo de coisa, em comparação com qualquer código processual looping é provável que você gravação.