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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top