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

Soma cumulativa sobre um conjunto de linhas no mysql


ATUALIZAÇÃO

O MySQL 8.0 introduz "funções de janela", funcionalidade equivalente às "funções de janela" do SQL Server (com particionamento e ordenação fornecidos pelo Transact-SQL OVER sintaxe) e "funções analíticas" do Oracle.

Manual de referência do MySQL 12.21 Funções de janela https://dev.mysql .com/doc/refman/8.0/en/window-functions.html

A resposta fornecida aqui é uma abordagem para versões do MySQL anteriores a 8.0.

RESPOSTA ORIGINAL

O MySQL não fornece a função analítica de tipo que você usaria para obter uma "soma cumulativa" em execução, como as funções analíticas disponíveis em outros DBMS (como Oracle ou SQL Server).

Mas, é possível emular algumas funções analíticas, usando o MySQL.

Existem (pelo menos) duas abordagens viáveis:

Uma é usar uma subconsulta correlacionada para obter o subtotal. Essa abordagem pode ser cara em conjuntos grandes e complicada se os predicados na consulta externa forem complicados. Realmente depende de quão complicado é "várias junções em várias tabelas". (Infelizmente, o MySQL também não suporta CTEs.)

A outra abordagem é fazer uso de variáveis ​​de usuário do MySQL, para fazer algum processamento de quebra de controle. O "truque" aqui é classificar os resultados da sua consulta (usando um ORDER BY) e, em seguida, envolver sua consulta em outra consulta.

Vou dar um exemplo da última abordagem.

Devido à ordem em que o MySQL executa as operações, o cumulative_total coluna precisa ser computada antes do valor de id e day da linha atual são salvos em variáveis ​​de usuário. É mais fácil colocar esta coluna primeiro.

A visualização inline com o alias de i (na consulta abaixo) serve apenas para inicializar as variáveis ​​do usuário, caso elas já estejam definidas na sessão. Se eles já tiverem valores atribuídos, queremos ignorar seus valores atuais e a maneira mais fácil de fazer isso é inicializá-los.

Sua consulta original é colocada entre parênteses e recebe um alias, c no exemplo abaixo. A única alteração em sua consulta original é a adição de uma cláusula ORDER BY, para que possamos ter certeza de que processamos as linhas da consulta em sequência.

A seleção externa verifica se o id e day valor da linha atual "corresponde" à linha anterior. Se sim, adicionamos o amount da linha atual para o subtotal cumulativo. Se eles não corresponderem, redefinimos o subtotal cumulativo para zero e adicionamos o valor da linha atual (ou, mais simplesmente, atribuímos o valor da linha atual).

Após termos feito o cálculo do total cumulativo, salvamos o id e day valores da linha atual em variáveis ​​de usuário, para que estejam disponíveis quando processarmos a próxima linha.

Por exemplo:
SELECT IF(@prev_id = c.id AND @prev_day = c.day
         ,@cumtotal := @cumtotal + c.amount
         ,@cumtotal := c.amount) AS cumulative_total
     , @prev_id  := c.id  AS `id`
     , @prev_day := c.day AS `day`
     , c.hr
     , c.amount AS `amount'
  FROM ( SELECT @prev_id  := NULL
              , @prev_day := NULL
              , @subtotal := 0
       ) i
  JOIN (

         select id, day, hr, amount from
         ( //multiple joins on multiple tables)a
         left join
         (//unions on multiple tables)b
         on a.id=b.id

         ORDER BY 1,2,3
       ) c

Se for necessário retornar as colunas em uma ordem diferente, com o total cumulativo como a última coluna, uma opção é envolver toda essa instrução em um conjunto de parênteses e usar essa consulta como uma visualização em linha:
SELECT d.id
     , d.day
     , d.hr
     , d.amount
     , d.cumulative_total
FROM (
       // query from above
     ) d