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

ATUALIZAR TABELA COM SOMA


Acionadores são provavelmente o que você quer. No entanto, fazer isso funcionar de forma adequada e eficiente será feio. Provavelmente é melhor não armazenar o saldo em cada linha se você for inserir linhas em datas anteriores com tanta frequência; em vez disso, use consultas ou visualizações para encontrar o equilíbrio. Para encontrar o saldo em uma determinada data, una-o com as linhas de datas anteriores e some o depósito líquido, agrupando pelo ID da transação atual:
CREATE VIEW pettybalance
  AS SELECT SUM(older.pc_in - older.pc_out) AS balance, 
            current.pc_id AS pc_id,  -- foreign key
            current.pc_date AS `date`
       FROM pettycash AS current
         JOIN pettycash AS older
           ON current.pc_date > older.pc_date 
              OR (current.pc_date = older.pc_date AND current.pc_id >= older.pc_id)
       GROUP BY current.pc_id
;

Eu também restrinjo older.pc_id seja menor que current.pc_id para corrigir uma ambiguidade relativa ao esquema e ao cálculo do saldo. Desde o pc_date não é único, você pode ter várias transações para uma determinada data. Se for esse o caso, qual deve ser o saldo para cada transação? Aqui assumimos que uma transação com um ID maior vem depois de uma transação com um ID menor, mas que tem a mesma data. Mais formalmente, usamos a ordenação

Observe que na visualização, usamos uma ordem ≥ baseada em>:

Depois de tentar fazer com que os gatilhos funcionem corretamente, vou recomendar nem tentar. Devido a bloqueios internos de tabelas ou linhas ao inserir/atualizar, você precisa mover a coluna de saldo para uma nova tabela, embora isso não seja muito oneroso (renomear pettycash para pettytransactions , crie um novo pettybalance (balance, pc_id) table e crie uma visão chamada pettycash do que une pettytransactions e pettybalance em pc_id ). O principal problema é que os corpos dos gatilhos são executados uma vez para cada linha criada ou atualizada, o que os tornará incrivelmente ineficientes. Uma alternativa seria criar um procedimento armazenado para atualizar colunas, que você pode chamar após inserir ou atualizar. Um procedimento é mais eficiente ao obter saldos do que uma exibição, mas mais frágil, pois cabe aos programadores atualizar os saldos, em vez de deixar o banco de dados lidar com isso. Usar uma visualização é o design mais limpo.
DROP PROCEDURE IF EXISTS update_balance;
delimiter ;;
CREATE PROCEDURE update_balance (since DATETIME)
BEGIN
    DECLARE sincebal DECIMAL(10,2);
    SET sincebal = (
          SELECT pc_bal 
            FROM pettycash AS pc 
            WHERE pc.pc_date < since
            ORDER BY pc.pc_date DESC, pc.pc_id DESC LIMIT 1
        );
    IF ISNULL(sincebal) THEN
      SET sincebal=0.0;
    END IF;
    UPDATE pettycash AS pc
      SET pc_bal=(
        SELECT sincebal+SUM(net) 
          FROM (
            SELECT pc_id, pc_in - pc_out AS net, pc_date
              FROM pettycash
              WHERE since <= pc_date 
          ) AS older
          WHERE pc.pc_date > older.pc_date
             OR (pc.pc_date = older.pc_date 
                 AND pc.pc_id >= older.pc_id)
      ) WHERE pc.pc_date >= since;
END;;
delimiter ;

Fora do tópico


Um problema com o esquema atual é o uso de Float s para armazenar valores monetários. Devido à forma como os números de ponto flutuante são representados, os números que são exatos na base 10 (ou seja, não têm uma representação decimal periódica) nem sempre são exatos como floats. Por exemplo, 0,01 (na base 10) estará mais próximo de 0,009999999776482582... ou 0,0100000000000000002081668... quando armazenado. É mais ou menos como 1/3 na base 3 é "0,1" mas 0,333333.... na base 10. Em vez de Float , você deve usar o Decimal modelo:
ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2);
ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2);

Se estiver usando uma visualização, solte pettycash.pc_bal . Se estiver usando um procedimento armazenado para atualizar pettycash.pc_bal , ele também deve ser alterado.