DAM - DAW - MP 0484 Bases de Dades
El sistema bancari és un dels que més protecció ha de tenir, de manera que no podem deixar a qualsevol programador realitzar tasques directes sobre les taules de la base de dades.
Avui dia, la majoria d'aplicacions treballen amb crides API per tal d'independitzar les aplicacions del SGBD, de minimitzar els errors en programació i maximitzar la seguretat del sistema.
Exercici proposat – Triggers
La proposta és anar creant triggers que automatitzin algunes de les tasques a realitzar amb les operacions bancàries.
Posteriorment, podrem programar procediments i funcions que faran ús d'aquests triggers i seran més senzills de programar.
Podem anar realitzant els triggers en el mateix ordre en què es proposen i, així, podrem provar-los fent operacions en les diferents taules.
Farem els següents triggers:
-
OFICINA:
BEFORE INSERTiBEFORE DELETE -
COMPTE:
BEFORE INSERT,AFTER INSERT,BEFORE UPDATE,AFTER UPDATEiBEFORE DELETE -
MOVIMENT:
BEFORE INSERT,AFTER INSERT,BEFORE UPDATEiBEFORE DELETE
Taula Oficina
INSERT
-
BEFORE INSERTAl crear el compte els camps
darrerCompteisaldoOficinaSEMPRE seran 0 => Posem els valors a zero. -
AFTER INSERTNo el programem. Res a fer.
START TRANSACTION;
INSERT INTO oficina(dOficina, adrOficina)
VALUES ('Una', 'Avda. Montilivi');
INSERT INTO oficina(dOficina, adrOficina, darrerCompte, saldoOficina)
VALUES ('Dues', 'Pl. Catalunya', 10, 900);
INSERT INTO oficina(dOficina, adrOficina, darrerCompte, saldoOficina)
VALUES ('Tres', 'C. Amunt', NULL, -1000);
SELECT * FROM oficina;
ROLLBACK;
INSERT INTO oficina(dOficina, adrOficina) VALUES ('Una', 'Avda. Montilivi');
Query OK, 1 row affected (0.034 sec)
INSERT INTO oficina(dOficina, adrOficina, darrerCompte, saldoOficina) VALUES ('Dues', 'Pl. Catalunya', 10, 900);
Query OK, 1 row affected (0.001 sec)
INSERT INTO oficina(dOficina, adrOficina, darrerCompte, saldoOficina) VALUES ('Tres', 'C. Amunt', NULL, -1000);
Query OK, 1 row affected (0.010 sec)
SELECT * FROM oficina;
+----------+----------+-----------------+--------------+--------------+
| cOficina | dOficina | adrOficina | darrerCompte | saldoOficina |
+----------+----------+-----------------+--------------+--------------+
| 1 | Una | Avda. Montilivi | 0 | 0.00 |
| 2 | Dues | Pl. Catalunya | 0 | 0.00 |
| 3 | Tres | C. Amunt | 0 | 0.00 |
+----------+----------+-----------------+--------------+--------------+
3 rows in set (0.000 sec)
Els valors darrerComptei saldoOficina són sempre 0.
UPDATE
-
BEFORE UPDATENo el programem. Res a fer.
-
AFTER UPDATENo el programem. Res a fer.
DELETE
-
BEFORE DELETENo permetem que s'eliminin registres de
oficina=> Provoquem ERROR (SIGNAL) -
AFTER DELETENo té sentit si no permetem
DELETEs
START TRANSACTION;
DELETE FROM oficina WHERE cOficina = 1;
ROLLBACK;
delete from oficina where cOficina = 1;
ERROR 1643 (02000): ERROR: No es poden eliminar Oficines
Fixeu-vos en l'ERROR que ens ha donat!
Taula Compte
INSERT
-
BEFORE INSERTAl crear el compte els camps
numeroOperacionsAdd,numeroOperacionsSubisaldoCompteSEMPRE seran 0 => Posem els valors a zeroPel valor pel número de Compte (
cCompte), anem a buscar a Oficina el campdarrerComptei assignem aquest incrementat en un => Agafem el valor calculat.Opcional: Ja que hem anat a buscar el valor
darrerComptea la taula Oficina, en cas que no existeixi la Oficina, podem provocar un ERROR personalitzat. En cas contrari donarà error el SGBDR per no trobar laPKdel campcOficina.No modifiquem, encara, el camp
darrerComptede la taulaOficina -
AFTER INSERTModificarem la taula Oficina segons els valors de l'
INSERT. Els camps a modificar són:darrerCompte
Ara sí, quan ja tenim la conformitat que tot és correcte, modifiquem el camp
darrerComptede la taulaOficina
START TRANSACTION;
INSERT INTO Oficina(cOficina) VALUES(9999);
INSERT INTO Compte(cOficina, cCompte, numeroOperacionsAdd, numeroOperacionsSub, saldoCompte)
VALUES (1,1,1,2,30.00);
INSERT INTO Compte(cOficina, cCompte, numeroOperacionsAdd, numeroOperacionsSub, saldoCompte)
VALUES (2,NULL,DEFAULT,0,250.00);
INSERT INTO Compte(cOficina) VALUES(2);
SELECT * FROM Oficina;
SELECT * FROM Compte;
ROLLBACK;
INSERT INTO Oficina(cOficina) VALUES(9999);
ERROR 1643 (02000): Oficina 9999 inexistent
INSERT INTO Compte(cOficina, cCompte, numeroOperacionsAdd, numeroOperacionsSub, saldoCompte)
VALUES (1,1,1,2,30.00);
Query OK, 1 row affected (0.000 sec)
INSERT INTO Compte(cOficina, cCompte, numeroOperacionsAdd, numeroOperacionsSub, saldoCompte)
VALUES (2,NULL,DEFAULT,0,250.00);
Query OK, 1 row affected (0.037 sec)
INSERT INTO Compte(cOficina) VALUES(2);
Query OK, 1 row affected (0.001 sec)
SELECT * FROM Oficina;
+----------+----------+-----------------+--------------+--------------+
| cOficina | dOficina | adrOficina | darrerCompte | saldoOficina |
+----------+----------+-----------------+--------------+--------------+
| 1 | Una | Avda. Montilivi | 1 | 0.00 |
| 2 | Dues | Pl. Catalunya | 2 | 0.00 |
| 3 | Tres | C. Amunt | 0 | 0.00 |
+----------+----------+-----------------+--------------+--------------+
SELECT * FROM Compte;
+----------+---------+---------------------+---------------------+-------------+
| cOficina | cCompte | numeroOperacionsAdd | numeroOperacionsSub | saldoCompte |
+----------+---------+---------------------+---------------------+-------------+
| 1 | 1 | 0 | 0 | 0.00 |
| 2 | 1 | 0 | 0 | 0.00 |
| 2 | 2 | 0 | 0 | 0.00 |
+----------+---------+---------------------+---------------------+-------------+
Mireu l'ordre en què s'executen els triggers amb les comandes
El primer INSERT, tot i no tenir totes les dades, executa el trigger BEFORE INSERT abans de fer les comprovacions i ja dona ERROR.
Fixeu-vos que per crear un Compte, només cal indicar el Codi d'oficina cOficina
Ignora la resta de camps que li passem.
També s'ha actualitzat darrerComte de la taula Oficina
UPDATE
-
BEFORE UPDATENo permetem canviar ni el
cOficinani elcCompte. En el cas d'intentar modificar-los cal generar un ERROR.Com que no admetem comptes amb saldo negatiu, en cas de posar un saldo inferior a 0 generarem un ERROR.
-
AFTER UPDATEEn el cas de modificar-se el saldoCompte, modificarem la taula
Oficinasegons els valors de l'UPDATE. El camp a modificar és:saldoOficina
START TRANSACTION;
UPDATE Compte SET cCompte = 10 where cCompte = 2;
UPDATE Compte SET saldoCompte = -10 where cCompte = 2;
UPDATE Compte SET saldoCompte = 30.00 WHERE cOficina = 1 AND cCompte = 1;
UPDATE Compte SET saldoCompte = 250.00 WHERE cOficina = 2 AND cCompte = 1;
UPDATE Compte SET saldoCompte = 200.00 WHERE cOficina = 2 AND cCompte = 2;
UPDATE Compte SET saldoCompte = 50.00 WHERE cOficina = 2 AND cCompte = 1;
SELECT * FROM Oficina;
SELECT * FROM Compte;
ROLLBACK;
UPDATE Compte SET cCompte = 10 where cCompte = 2;
ERROR 1643 (02000): ERROR: No es pot modificar el codi de la Oficina ni del Compte
UPDATE Compte SET saldoCompte = -10 where cCompte = 2;
ERROR 1643 (02000): ERROR: No es permetem saldos negatius
UPDATE Compte SET saldoCompte = 30.00 WHERE cOficina = 1 AND cCompte = 1;
Query OK, 1 row affected (0.001 sec)
UPDATE Compte SET saldoCompte = 250.00 WHERE cOficina = 2 AND cCompte = 1;
Query OK, 1 row affected (0.001 sec)
UPDATE Compte SET saldoCompte = 200.00 WHERE cOficina = 2 AND cCompte = 2;
Query OK, 1 row affected (0.000 sec)
UPDATE Compte SET saldoCompte = 50.00 WHERE cOficina = 2 AND cCompte = 1;
Query OK, 1 row affected (0.000 sec)
SELECT * FROM Oficina;
+----------+----------+-----------------+--------------+--------------+
| cOficina | dOficina | adrOficina | darrerCompte | saldoOficina |
+----------+----------+-----------------+--------------+--------------+
| 1 | Una | Avda. Montilivi | 1 | 30.00 |
| 2 | Dues | Pl. Catalunya | 2 | 250.00 |
| 3 | Tres | C. Amunt | 0 | 0.00 |
+----------+----------+-----------------+--------------+--------------+
SELECT * FROM Compte;
+----------+---------+---------------------+---------------------+-------------+
| cOficina | cCompte | numeroOperacionsAdd | numeroOperacionsSub | saldoCompte |
+----------+---------+---------------------+---------------------+-------------+
| 1 | 1 | 0 | 0 | 30.00 |
| 2 | 1 | 0 | 0 | 50.00 |
| 2 | 2 | 0 | 0 | 200.00 |
+----------+---------+---------------------+---------------------+-------------+
ROLLBACK;
També s'ha actualitzat saldoOficina de la taula Oficina
Vigileu que el saldoOficina ha de ser la suma dels saldos dels Comptes
La següent consulta no hauria de mostar cap fila
SELECT cOficina
FROM Oficina o
WHERE saldoOficina <> (SELECT SUM(saldoCompte)
FROM Compte c
WHERE c.cOficina = o.cOficina);
Empty set (0.042 sec)
DELETE
-
BEFORE DELETENo permetem que s'eliminin registres de
compte=> Provoquem ERROR (SIGNAL) -
AFTER DELETENo té sentit si no permetem
DELETEs
START TRANSACTION;
DELETE FROM Compte WHERE cOficina = 1 AND cCompte = 1;
ROLLBACK;
DELETE FROM Compte WHERE cOficina = 1 AND cCompte = 1;
ERROR 1643 (02000): ERROR: No es poden eliminar Comptes
Podriem canviar el criteri i permetre eliminar només Comptes amb Saldo 0 o bé, abans d'eliminar un Compte, guardar les dades en una altra taula
Taula Moviment
INSERT
-
BEFORE INSERTEl valor del número de moviment (
numeroMoviment) el busquem escollint el màxim + 1. Si és el primer, li posem 1.Podem aprofitar per comprovar si existeix el Compte i en cas contrari generar un ERROR personalitzat.
Sempre ha de quedar gravat com a
moment, el dia actual: CURRENT_DATE())No permetem que l'import d'una operació sigui 0 => Provoquem ERROR (
SIGNAL)Si no ens passen el camp
tipusMovimentllavors:-
si l'import és positiu valdrà Ingrés
-
si l'import és negatiu valdrà Reintegre
No permetem que l'import d'una operació faci que el saldo del compte sigui negatiu => Provoquem ERROR (
SIGNAL)Aquest darrer cas no cal contemplar-lo ja que, en modificar el
saldoCompteamb el triggerAFTER UPDATE, el triggerBEFORE UPDATEde la taula Compte generarà un error. -
-
AFTER INSERTModificarem la taula Compte segons els valors de l'
INSERT. Els camps a modificar són:-
numeroOperacionsAdd: quan l'importsigui positiu l'incrementem en 1. -
numeroOperacionsSub: quan l'importsigui negatiu l'incrementem en 1. -
saldoCompte: l'actualitzem segone l'import.
-
START TRANSACTION;
INSERT INTO Moviment(import) VALUES(0);
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(1,1,-100);
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(1,1,0);
INSERT INTO Moviment(cOficina, cCompte, numeroMoviment, import, moment, tipusMoviment)
VALUES(1,1,999,150, '1900-01-01', 'Ingres 1' );
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(1,1,-75);
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,1,175);
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,1,250);
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,2,750);
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,2,-250);
SELECT * FROM Oficina;
SELECT * FROM Compte ORDER BY cOficina, cCompte;
SELECT * FROM Moviment ORDER BY cOficina, cCompte, numeroMoviment;
ROLLBACK;
START TRANSACTION;
INSERT INTO Moviment(import) VALUES(0);
ERROR 1643 (02000): L´import no pot ser 0
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(1,1,-100);
ERROR 1643 (02000): ERROR: No es permetem saldos negatius
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(1,1,0);
ERROR 1643 (02000): L´import no pot ser 0
INSERT INTO Moviment(cOficina, cCompte, numeroMoviment, import, moment, tipusMoviment)
VALUES(1,1,999,150, '1900-01-01', 'Ingres 1' );
Query OK, 1 row affected (0.001 sec)
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(1,1,-75);
Query OK, 1 row affected (0.001 sec)
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,1,175);
Query OK, 1 row affected (0.000 sec)
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,1,250);
Query OK, 1 row affected (0.000 sec)
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,2,750);
Query OK, 1 row affected (0.000 sec)
INSERT INTO Moviment(cOficina, cCompte, import)
VALUES(2,2,-250);
Query OK, 1 row affected (0.001 sec)
SELECT * FROM Oficina;
+----------+----------+-----------------+--------------+--------------+
| cOficina | dOficina | adrOficina | darrerCompte | saldoOficina |
+----------+----------+-----------------+--------------+--------------+
| 1 | Una | Avda. Montilivi | 1 | 75.00 |
| 2 | Dues | Pl. Catalunya | 2 | 925.00 |
| 3 | Tres | C. Amunt | 0 | 0.00 |
+----------+----------+-----------------+--------------+--------------+
SELECT * FROM Compte ORDER BY cOficina, cCompte;
+----------+---------+---------------------+---------------------+-------------+
| cOficina | cCompte | numeroOperacionsAdd | numeroOperacionsSub | saldoCompte |
+----------+---------+---------------------+---------------------+-------------+
| 1 | 1 | 1 | 1 | 75.00 |
| 2 | 1 | 2 | 0 | 425.00 |
| 2 | 2 | 1 | 1 | 500.00 |
+----------+---------+---------------------+---------------------+-------------+
SELECT * FROM Moviment ORDER BY cOficina, cCompte, numeroMoviment;
+----------+---------+----------------+---------+------------+---------------+
| cOficina | cCompte | numeroMoviment | import | moment | tipusMoviment |
+----------+---------+----------------+---------+------------+---------------+
| 1 | 1 | 1 | 150.00 | 2023-02-25 | Ingres 1 |
| 1 | 1 | 2 | -75.00 | 2023-02-25 | Reintegre |
| 2 | 1 | 1 | 175.00 | 2023-02-25 | Ingrés |
| 2 | 1 | 2 | 250.00 | 2023-02-25 | Ingrés |
| 2 | 2 | 1 | 750.00 | 2023-02-25 | Ingrés |
| 2 | 2 | 2 | -250.00 | 2023-02-25 | Reintegre |
+----------+---------+----------------+---------+------------+---------------+
ROLLBACK;
Fixeu-vos com no agafa els valors de numeroMoviment i moment que li passem nosaltres.
Els únics camps necessaris són cOficina, cCompte, import.
S'han actualitzat els saldos de l'oficina i del compte.
El saldo negatiu no es permet, però pel trigger del Compte
UPDATE
-
BEFORE UPDATENo permetem que es modifiquin registres de
moviment=> Provoquem ERROR (SIGNAL) -
AFTER UPDATENo té sentit si no permetem
UPDATEs
START TRANSACTION;
UPDATE Moviment SET import = 0;
ROLLBACK;
UPDATE Moviment SET import = 0;
ERROR 1643 (02000): ERROR: No es poden modificar Moviments
No permetem modificar moviments.
En cas de poder-los modificar caldria:
-
vigilar si modifiquem o no el codi d'Oficina o de Compte
-
actualitzar el Compte antic i actualitzar el nou.
les oficines ja s'actualitzaran automàticament.
DELETE
-
BEFORE DELETENo permetem que s'eliminin registres de
moviment=> Provoquem ERROR (SIGNAL) -
AFTER DELETENo té sentit si no permetem
DELETEs
START TRANSACTION;
DELETE FROM Moviment;
ROLLBACK;
DELETE FROM Moviment;
ERROR 1643 (02000): ERROR: No es poden eliminar Moviments
No permetem eliminar moviments.
