sql >> Base de Datos >  >> RDS >> Mysql

ACTUALIZAR TABLA CON SUMA

Activadores probablemente quieras que quieras. Sin embargo, hacer que esto funcione de manera adecuada y eficiente será feo. Probablemente sea mejor no almacenar el saldo en cada fila si va a insertar filas en fechas anteriores con tanta frecuencia; en su lugar, use consultas o vistas para encontrar el equilibrio. Para encontrar el saldo en una fecha en particular, únalo con las filas de fechas anteriores y sume el depósito neto, agrupándolo por el ID de transacción actual:

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
;

También restrinjo older.pc_id ser menor que current.pc_id para corregir una ambigüedad relacionada con el esquema y el cálculo del saldo. Desde el pc_date no es único, podría tener varias transacciones para una fecha determinada. Si ese es el caso, ¿cuál debería ser el saldo de cada transacción? Aquí asumimos que una transacción con una identificación más grande viene después de una transacción con una identificación más pequeña pero que tiene la misma fecha. Más formalmente, usamos la ordenación

Tenga en cuenta que en la vista, usamos un orden ≥ basado en>:

Después de intentar que los disparadores funcionen correctamente, recomendaré que ni siquiera lo intentes. Debido a bloqueos internos de tablas o filas al insertar/actualizar, debe mover la columna de saldo a una nueva tabla, aunque esto no es demasiado oneroso (cambie el nombre de pettycash a pettytransactions , cree un nuevo pettybalance (balance, pc_id) y crea una vista llamada pettycash que se une a pettytransactions y pettybalance en pc_id ). El principal problema es que los cuerpos desencadenantes se ejecutan una vez por cada fila creada o actualizada, lo que hará que sean increíblemente ineficientes. Una alternativa sería crear un procedimiento almacenado para actualizar columnas, a las que puede llamar después de insertar o actualizar. Un procedimiento es más eficaz al obtener saldos que una vista, pero es más frágil ya que depende de los programadores actualizar los saldos, en lugar de dejar que la base de datos los maneje. Usar una vista es el diseño más limpio.

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 ;

Fuera del tema

Un problema con el esquema actual es el uso de Float s para almacenar valores monetarios. Debido a cómo se representan los números de coma flotante, los números que son exactos en base 10 (es decir, no tienen una representación decimal periódica) no siempre son exactos como flotantes. Por ejemplo, 0.01 (en base 10) estará más cerca de 0.009999999776482582... o 0.0100000000000000002081668... cuando se almacena. Es como si 1/3 en base 3 fuera "0,1" pero 0,333333... en base 10. En lugar de Float , debe usar el Decimal tipo:

ALTER TABLE pettycash MODIFY pc_in DECIMAL(10,2);
ALTER TABLE pettycash MODIFY pc_out DECIMAL(10,2);

Si usa una vista, suelte pettycash.pc_bal . Si utiliza un procedimiento almacenado para actualizar pettycash.pc_bal , también debería modificarse.