Cálculo do MySQL de diferentes linhas na mesma coluna

Um, o problema

Existe uma mesa como esta:

encontro montante
31/12/2015 3000
22/01/2016 3100
23/01/2016 3100
24/01/2016 3100
25/01/2016 3100
26/01/2016 3100
27/01/2016 3100
28/01/2016 3100
29/01/2016 3100
30/01/2016 3100
31/01/2016 3300
01/02/2016 3400
02/02/2016 3500

Deseja obter resultados como os seguintes:

ano mês diferença
2016 1 300
2016 2 200

Escreva instruções SQL.

Pode-se adivinhar a partir do resultado que se trata de encontrar a diferença entre o valor acumulado de cada mês e do mês anterior, onde o valor do montante já é o valor acumulado, por isso precisa ser calculado novamente.

É muito simples à primeira vista, não é apenas agrupar estatísticas por ano e mês?

Se você pensar bem, não é tão fácil quanto você pensa. O mais importante é calcular a diferença entre as linhas. É muito fácil calcular a diferença entre as colunas no MySQL. A dificuldade está em calcular a diferença entre as linhas , o que requer um pequeno truque. , Converta valores de coluna em valores de linha por meio de variáveis ​​e subconsultas MySQL.

Nota: Para negócios de alta concorrência, geralmente não colocamos tais cálculos no MySQL, tentamos processá-los na camada de aplicativo ou usamos estatísticas diretamente, porque proteger o banco de dados em negócios de alta concorrência é nossa responsabilidade importante.

Claro, se forem apenas alguns relatórios offline ou serviços estatísticos, é claro que não há problema, porque relatórios offline e outros serviços podem ser usados, então as dicas a seguir ainda podem ser entendidas.

Dois, importar dados

Primeiro crie a tabela:

CREATE TABLE `stat_year`  (
  `stat_date` date NULL DEFAULT NULL,
  `amount` int UNSIGNED NULL DEFAULT NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

Carregue os dados, ignorando o cabeçalho da primeira linha:

load data local infile 'data.txt' into table amount fields terminated by '\t' ignore 1 lines;

Dados após importação

Três, use variáveis ​​para salvar o valor anterior

Vamos primeiro entender como o MySQL usa variáveis ​​em instruções SQL.

SELECT
    tmp.stat_date,
    tmp.current_amount,
    tmp.pre,
    ( tmp.current_amount - tmp.pre ) AS diff 
FROM
    (
    SELECT
        stat_date,
        amount AS current_amount,
        @pre_amount AS pre,
        @pre_amount := sp.amount 
    FROM
        stat_pay sp,
    ( SELECT @pre_amount := 0 ) AS pre_temp 
    ) AS tmp;

Use variáveis ​​para salvar o valor anterior

Em primeiro lugar, as variáveis ​​do usuário no MySQL começam com @, e as variáveis ​​do sistema começam com @@. A atribuição usa: =

Então, em sql

( SELECT @pre_amount := 0 ) AS pre_temp 

É equivalente a definir uma variável de usuário @pre_amount e inicializar seu valor para 0.

A parte da subconsulta da primeira instrução from é equivalente a não selecionar uma linha. Primeiro, o valor de acessar @pre_amount é usado como o valor anterior e um alias pre é dado, e então o valor da linha atual é atribuído a @pre_amount.

Agora a consulta externa é muito mais fácil de entender, que consiste em verificar o valor da linha atual, o valor da linha anterior da linha atual e a diferença entre o valor da linha atual e o valor da linha anterior.

Vamos dar uma olhada no resultado de explicação da instrução SQL acima:

explicar

A descrição de saída de explain:

  1. O id é a identificação de cada select, quanto maior o id, maior a prioridade, mais ele será executado primeiro, e aqueles com o mesmo id serão executados de cima para baixo.
  2. select_type: PRIMARY representa a última seleção executada; DERIVED representa a subconsulta na instrução from
  3. tabela significa a tabela usada, significa a tabela derivada obtida usando id 2

Agora, vamos olhar para a saída de explain novamente, é muito mais claro:

Primeiro encontre o maior id, o maior id 3, execute primeiro, podemos ver que select_type é DERIVED, o que significa que irá gerar uma tabela derivada, na verdade, é equivalente a definir uma variável @pre_amount em uma tabela, desta tabela O alias é pre_temp, que é a subconsulta na segunda instrução from.

Existem dois com um id de 2, e ambos select_type são DERIVADOS, porque esses dois são subconsultas na primeira instrução from.
De cima para baixo, vemos que a tabela na linha 2 é, indicando que ela usa a tabela derivada gerada pela consulta com id 3, que é a tabela pre_temp. O tipo é sistema, o que significa que esta tabela possui apenas 1 linha, que também pode ser vista nas linhas.

A terceira linha da tabela é sp, o que significa que a tabela real de sp é usada diretamente e sp é o apelido de stat_pay.

Finalmente, select_type com id 1 é PRIMARY, o que significa que esta é a consulta externa executada por último, e table significa que a tabela usada é uma tabela derivada obtida de uma consulta com id 2.

Quarto, a solução final

Como queremos agrupar por ano e mês, e temos apenas data, podemos calcular o valor de ano e mês por meio de substring ou date_format.

SELECT substring(stat_date,1,4) AS stat_year,substring(stat_date,6,2) AS mon FROM stat_pay;
SELECT date_format(stat_date,'%Y') AS stat_year,DATE_FORMAT(stat_date,'%m') AS mon FROM stat_pay;

Vamos dar uma olhada em nosso SQL final:

SELECT
    tmp.stat_year,
    tmp.mon,
    tmp.current_amount,
    tmp.pre,
    ( tmp.current_amount - tmp.pre ) AS diff 
FROM
    (
    SELECT
        total_tmp.stat_year,
        total_tmp.mon,
        total_tmp.total_amount AS current_amount,
        @pre_amount AS pre,
        @pre_amount := total_tmp.total_amount 
    FROM
        (
        SELECT
            substring( stat_date, 1, 4 ) AS stat_year,
            substring( stat_date, 6, 2 ) AS mon,
            max( amount ) AS total_amount 
        FROM
            stat_pay 
        GROUP BY
            stat_year,
            mon 
        ) AS total_tmp,
    ( SELECT @pre_amount := 0 ) AS pre_temp 
    ) AS tmp;

resultado

Se você é um perfeccionista e deseja exatamente o mesmo resultado e não deseja o prefixo 0 no ano e no mês, pode converter a string em um número inteiro de uma das seguintes maneiras:

substring( stat_date, 1, 4 ) + 0 AS stat_year
convert(substring( stat_date, 6, 2 ),unsigned integer) as stat_year
cast(substring( stat_date, 6, 2 ) as unsigned integer) as stat_year

Por fim, filtre a primeira linha por meio da instrução de limite para obter o resultado final:

resultado perfeito

V. Resumo

Podemos criar uma tabela derivada para armazenar uma variável temporária usando select na instrução from e, em seguida, manipular essa variável na instrução select.

Por analogia, podemos também armazenar várias variáveis ​​na tabela temporária, não apenas cálculos entre a mesma coluna, mas também cálculos em colunas diferentes.

Acho que você gosta

Origin blog.csdn.net/trayvontang/article/details/103427864
Recomendado
Clasificación