jueves, 7 de septiembre de 2017

SQLite – Triggers: explicación y ejemplos

Un trigger es un evento que se lanza cuando ocurre algo determinado en una base de datos y se ejecuta una sentencia SQL. Ese “algo” que ocurre puede ser que se borre un registro (fila) de una determinada tabla, que se agregue un registro o que se modifique uno o más campos de una tabla, es decir, un INSERT, DELETE o UPDATE.

¿Cuándo se declara el trigger? Puede declararse en cualquier momento y siempre de acuerdo a las necesidades del caso, siendo lo normal hacerlo al momento de crear las tablas, como los índices y constraints.

No es necesario que las tablas actuantes en el trigger estén relacionadas mediante una Foreign Key.

Existe una IDE para SQLite muy práctica para este tipo de cosas, especialmente hasta que le tomemos la mano, nos es de gran ayuda, que es SQLiteStudio (está en inglés) y es de código abierto.

Uno puede, no obstante, prescindir de los triggers de SQLite y hacerlo a mano, es decir, mediante programación, en mi caso, con Free Pascal desde Lazarus, de hecho lo estuve haciendo hasta ahora, por “seguridad”, por preferir tener el control total especialmente en cuanto a la validación de por ejemplo valores duplicados, todavía lo hago, no me llevo bien con el manejo de excepciones, además SQLite carece de Stored Procedures, de momento son excusas válidas. Pero para borrar un registro en otra tabla, actualizar un dato, o agregar, casos simples, empecé a utilizar triggers, más precisamente hace dos días y por suerte además de funcionar correctamente, no es algo difícil de implementar.

Los ejemplos son casos reales de un programa de control de proveedores e insumos.


INSERT:

Un trigger que cree un registro en otra tabla cada vez que el usuario da de alta un insumo.

CREATE TRIGGER agregoinsu
         AFTER INSERT
            ON prod
BEGIN
    INSERT INTO movinsu (
                            idcompra,
                            idprod,
                            fecha,
                            cantidad,
                            precio,
                            total,
                            saldo
                        )
                        VALUES (
                            0,
                            new.id,
                            '2016-01-01',
                            new.ini,
                            0,
                            0,
                            0
                        );
END;


Al trigger lo nombré “agregoinsu”, luego hay que indicar cuando se dispara el trigger, antes o después y antes o después de ¿qué?, en este caso después (AFTER) de que se produjo el INSERT. ¿en qué tabla?, ON prod (en la tabla prod). Nota: la tabla prod debería llamarse insumos, pero se llama prod por motivos que no vale la pena aclarar. Hasta acá definimos cuando se lanza el trigger, ahora debemos indicar que se hace y eso lo hacemos mediante sentencias SQL comprendidas entre BEGIN y END; En este caso se indica que se inserte un registro en la tabla “movinsu” que posee un campo autoincremental “id” que se omite porque se encarga SQLite, el resto de las columnas o campos los especifico. Ahora debo definir los valores de esa nueva fila en “movinsu” y acá aparece la palabra new. Para triggers de inserción, se utiliza new, para triggers de eliminación se utiliza old y para triggers de actualización se pueden utilizar ambos (new y old). “new.id” significa que el nuevo registro en movinsu, en la columna idprod, irá el valor del nuevo (new) id (prod.id) y en la columna cantidad ira el valor “new.ini” que viene de prod.ini. Resumiendo, cada vez que se agregue, o dé de alta, un insumo (prod) se insertará una fila en la tabla movinsu con los valores especificados.

Este trigger debe definirse sobre la tabla que lanza el evento, en este caso, la tabla prod.

DELETE:

Lo contrario al ejemplo anterior, un trigger que elimine un registro en la tabla movinsu cuando se elimina un insumo de la tabla prod.

CREATE TRIGGER borroinsu
         AFTER DELETE
            ON prod
BEGIN
    DELETE FROM movinsu
          WHERE (idprod = old.id) AND
                (idcompra = 0);
END;


Este trigger es muy simple, AFTER (después) de DELETE (borrar) un registro ON (en) la tabla prod se ejecuta la sentencia SQL comprendida entre BEGIN y END, borrar de la tabla movinsu donde “old.id” sea igual a idprod (de la tabla movinsu). La otra condición idcompra=0 es porque con ese ID identifico el stock inicial; en realidad podría obviar esto, ya que antes de permitir borrar un insumos chequeo primero que no tenga movimientos registrados, si los tiene no permito su eliminación, pero por las dudas prefiero comprobarlo dos veces y no dejar que un trigger se dispare y borre alegremente todos los movimientos de un insumo, no nos olvidemos que cualquiera con un poco de conocimientos puede abrir la tabla y borrar un insumo y el trigger se dispara igual, por ejemplo, un usuario intenta borrar un insumo y el programa no lo deja porque tiene 20 registraciones, el usuario debe primero borrar todas las registraciones y luego borrar el insumo, puede tentarse de meter mano a la base de datos y borrarlo, pues bien, en este caso, el trigger con el condicional (idcompra=0) solo borrará el registro correspondiente al stock inicial dejando “vivas” las otras registraciones. Claro que si lo que se pretende es que el trigger arrase con todo quedaría así:

CREATE TRIGGER borroinsu
         AFTER DELETE
            ON prod
BEGIN
    DELETE FROM movinsu
          WHERE (idprod = old.id);
END;



“old.id” es el campo “id” del registro que se borró. Como es un trigger del tipo delete, solo se puede utilizar old para referenciar un campo.

UPDATE:

En update se puede usar tanto old como new para referenciar campos. Este trigger se lanza cuando se modifica el stock inicial de un insumo (campo “ini”) en la tabla prod y modifica el campo “cantidad” en la tabla movinsu.

CREATE TRIGGER modificoinsu
         AFTER UPDATE OF ini
            ON prod
BEGIN
    UPDATE movinsu
       SET cantidad = new.ini
     WHERE (idprod = new.id) AND
           (idcompra = 0);
END;


Traduciendo un poco, se lanza después (AFTER) de una modificación (UPDATE) del campo “ini” de la tabla prod (ON prod) y se ejecuta la actualización (UPDATE) de la tabla movinsu, se establece (SET) el campo “cantidad” con el valor de “new.ini” (el nuevo stock inicial) y la claúsula WHERE es la misma que el ejemplo anterior. Si bien el campo “id” no cambia, se debe referenciarlo con new o con old.

Son tres ejemplos sencillos para iniciarse en el tema triggers.

Documentación consultada: (en inglés)

https://sqlite.org/lang_createtrigger.html

https://www.tutorialspoint.com/sqlite/sqlite_triggers.htm

No hay comentarios:

Publicar un comentario