Salta el contingut

DAM - DAW - MP 0484 Bases de Dades

Exercici proposat – Procediments i Funcions

La proposta és anar creant procediments i funcions per tal que els pugui utilitzar el treballador de l’oficina (userbanc). La proposta de procediments i funcions a realitzar és la següent:

Si voleu tenir ja algunes dades inserides per a fer proves...

Podeu agafar el fitxer adjunt o utilitzar les següents instruccions

Inserts per la base del Banc
DELETE FROM moviment;
DELETE FROM compte;
DELETE FROM oficina;

INSERT INTO `oficina` VALUES 
    (1,'Una','Avda. Montilivi',1,30.00),
    (2,'Dues','Pl. Catalunya',2,450.00),
    (3,'Tres','C. Amunt',0,0.00);

INSERT INTO `compte` VALUES 
    (1,1,1,2,30.00),
    (2,1,3,3,250.00),
    (2,2,2,1,200.00);

INSERT INTO `moviment` VALUES 
    (1,1,1,200.00,CURRENT_DATE() - INTERVAL 12 DAY,'Ingrés'),
    (1,1,2,-70.00,CURRENT_DATE() - INTERVAL 7 DAY,'Reintegre'),
    (1,1,3,-100.00,CURRENT_DATE() - INTERVAL 1 DAY,'Traspas a 1234 0002 15 0000000001'),
    (2,1,1,100.00,CURRENT_DATE() - INTERVAL 13 DAY,'Ingrés'),
    (2,1,2,350.00,CURRENT_DATE() - INTERVAL 8 DAY,'Ingrés'),
    (2,1,3,-50.00,CURRENT_DATE() - INTERVAL 6 DAY,'Reintegre'),
    (2,1,4,-125.00,CURRENT_DATE() - INTERVAL 5 DAY,'Reintegre'),
    (2,1,5,100.00,CURRENT_DATE() - INTERVAL 1 DAY,'Traspas de 1234 0001 75 0000000001'),
    (2,1,6,-125.00,CURRENT_DATE(),'Traspas a 1234 0002 11 0000000002'),
    (2,2,1,90.00,CURRENT_DATE() - INTERVAL 8 DAY,'Ingrés'),
    (2,2,2,-15.00,CURRENT_DATE() - INTERVAL 1 DAY,'Reintegre'),
    (2,2,3,125.00,CURRENT_DATE(),'Traspas de 1234 0002 15 0000000001');

LlistaOficines - Procediment

  • Mostra el codi, nom i adreça de les oficines ordenat segons el paràmetre: 1: Codi, Altre: Nom

    SELECT

    Exemple d'ús i execució
    CALL LlistaOficines(0);
    +------+-------+-----------------+
    | Codi | Nom ^ | Adreça          |
    +------+-------+-----------------+
    |    2 | Dues  | Pl. Catalunya   |
    |    3 | Tres  | C. Amunt        |
    |    1 | Una   | Avda. Montilivi |
    +------+-------+-----------------+
    
    CALL LlistaOficines(1);
    +--------+------+-----------------+
    | Codi ^ | Nom  | Adreça          |
    +--------+------+-----------------+
    |      1 | Una  | Avda. Montilivi |
    |      2 | Dues | Pl. Catalunya   |
    |      3 | Tres | C. Amunt        |
    +--------+------+-----------------+
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE PROCEDURE LlistaOficines(ordre INT) 
    SQL SECURITY DEFINER 
    BEGIN 
        IF ordre = 1 THEN 
            SELECT cOficina "Codi ^", dOficina "Nom", adrOficina "Adreça"
            FROM oficina
            ORDER BY cOficina;
        ELSE
            SELECT cOficina "Codi", dOficina "Nom ^", adrOficina "Adreça"
            FROM oficina
            ORDER BY dOficina;
        END IF;
    END
    //
    DELIMITER ;
    CALL LlistaOficines(0);
    CALL LlistaOficines(1);
    

MostraOficina - Procediment

  • Mostra el codi, nom i adreça de la oficina que passem com a paràmetre.

    SELECT

    Exemple d'ús i execució
    CALL MostraOficina(2);
    +------+------+---------------+
    | Codi | Nom  | Adreça        |
    +------+------+---------------+
    |    2 | Dues | Pl. Catalunya |
    +------+------+---------------+
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE PROCEDURE MostraOficina(
        p_cOficina TYPE OF Oficina.cOficina
    )
    BEGIN
        DECLARE v_dOficina TYPE OF Oficina.dOficina;
        DECLARE v_adrOficina TYPE OF Oficina.adrOficina;
        DECLARE EXIT HANDLER FOR NOT FOUND SELECT 'Oficina Inexistent' AS "Missatge";
        SELECT dOficina, adrOficina
            INTO v_dOficina, v_adrOficina
            FROM oficina
            WHERE cOficina = p_cOficina;
        SELECT p_cOficina "Codi", v_dOficina "Nom", v_adrOficina "Adreça";
    END
    //
    DELIMITER ;
    CALL MostraOficina(2);
    CALL MostraOficina(12);
    

DigitControlBO - Funció

  • Donat el número de banc i el d’oficina, retorna el dígit de control. El del nostre Banc ens imaginarem que és l'1234.

    Càlcul dígits de Control (La Sexta)

    Crida a una altra funció.

    Exemple d'ús i execució
    SELECT DigitControlBO(1234, 1111);
    -- Ha de donar 1
    SELECT DigitControlBO(1234, 2222);
    -- Ha de donar 7
    
    Possible solució

    No utilitzarem FOR, ho deixarem per la funció DigitControlCte.

    Treballant amb CHAR - LPAD
    DELIMITER //
    CREATE OR REPLACE FUNCTION DigitControlBO(
        p_cBanc TYPE OF Oficina.cOficina,
        p_cOficina TYPE OF Oficina.cOficina
    )
    RETURNS INT
    BEGIN
        -- DECLARACIÓ DE VARIABLES LOCALS
        DECLARE v_digit INT;
        DECLARE v_cBanc CHAR(4);
        DECLARE v_cOficina CHAR(4);
        DECLARE v_A INT DEFAULT 0;
        DECLARE v_B INT DEFAULT 0;
        DECLARE v_C INT;
        -- CODI DE LA FUNCIÓ
        SET v_cBanc = LPAD(p_cBanc, 4, '0');
        SET v_digit = substring(v_cBanc, 1, 1);
        SET v_A = v_digit * 4;
        SET v_digit = substring(v_cBanc, 2, 1);
        SET v_a = v_A + v_digit * 8;
        SET v_digit = substring(v_cBanc, 3, 1);
        SET v_a = v_A + v_digit * 5;
        SET v_digit = substring(v_cBanc, 4, 1);
        SET v_a = v_A + v_digit * 10;
    
        SET v_cOficina = LPAD(p_cOficina, 4, '0');
        SET v_digit = substring(v_cOficina, 1, 1);
        SET v_B = v_digit * 9;
        SET v_digit = substring(v_cOficina, 2, 1);
        SET v_B = v_B + v_digit * 7;
        SET v_digit = substring(v_cOficina, 3, 1);
        SET v_B = v_B + v_digit * 3;
        SET v_digit = substring(v_cOficina, 4, 1);
        SET v_B = v_B + v_digit * 6;
    
        SET v_C = v_A + v_B;
        SET v_C = 11 - (v_C % 11);
        IF v_C = 10 THEN
            SET v_C = 1;
        ELSEIF v_C = 11 THEN
            SET v_C = 0;
        END IF;
        RETURN v_C;
    END
    //
    DELIMITER ;
    SELECT DigitControlBO(1234, 1111); -- Ha de donar 1
    SELECT DigitControlBO(1234, 2222); -- Ha de donar 7
    
    Treballant amb INT - TRUNCATE
    DELIMITER //
    CREATE OR REPLACE FUNCTION DigitControlBO(pBanc INT, pOficina INT)
    RETURNS INT
    DETERMINISTIC 
    BEGIN 
        -- DECLARACIÓ DE VARIABLES LOCALS
        DECLARE vDigit INT; 
        DECLARE vA, vB INT DEFAULT 0;
        DECLARE vC INT;
        -- CODI DE LA FUNCIÓ
        SET vA = TRUNCATE(pBanc / 1000, 0) * 4; -- Primer Digit
        SET pBanc = pBanc % 1000;
        SET vA = vA + TRUNCATE(pBanc / 100, 0) * 8; -- Segon Digit
        SET pBanc = pBanc % 100;
        SET vA = vA + TRUNCATE(pBanc / 10, 0) * 5; -- Segon Digit
        SET pBanc = pBanc % 10;
        SET vA = vA + TRUNCATE(pBanc / 1, 0) * 10; -- Segon Digit
    
        SET vB = TRUNCATE(  pOficina / 1000, 0) * 9; -- Primer Digit
        SET pOficina = pOficina % 1000;
        SET vB = vB + TRUNCATE(pOficina / 100, 0) * 7; -- Segon Digit
        SET pOficina = pOficina % 100;
        SET vB = vB + TRUNCATE(pOficina / 10, 0) * 3; -- Segon Digit
        SET pOficina = pOficina % 10;
        SET vB = vB + TRUNCATE(pOficina / 1, 0) * 6; -- Segon Digit
        SET vC = (vA + vB) % 11;
        SET vDigit = 11 - vC;
        IF vDigit = 10 THEN
            SET vDigit = 1;
        ELSEIF vDigit = 11 THEN
            SET vDigit = 0;
        END IF;
        -- RETORN DEL VALOR
        RETURN vDigit;
    END
    //
    DELIMITER ;
    SELECT DigitControlBO(1234, 1111); -- Ha de donar 1
    SELECT DigitControlBO(1234, 2222); -- Ha de donar 7
    

DigitControlCte - Funció

  • Donat el número de compte, retorna el dígit de control del Cte.

    Càlcul dígits de Control (La Sexta)

    Utilitzeu com a tipus de paràmetre BIGINT

    DETERMINISTIC - SET - IF / ELSEIF - CASE - FOR - TRUNCATE - MOD

    Exemple d'ús i execució
    SELECT DigitControlCte(0123456789);
    -- Ha de donar 1
    SELECT DigitControlCte(1234567890);
    -- Ha de donar 6
    
    Possible solució

    Utilitzarem FOR, sabent que les posicions es multipliquen per 2n%11.

    Treballant amb CHAR - LPAD
    DELIMITER //
    CREATE OR REPLACE function DigitControlCte(
        p_cCompte TYPE OF Compte.cCompte
    )
    RETURNS INT
    BEGIN
        DECLARE v_cCompte CHAR(10);
        DECLARE v_A INT DEFAULT 0;
        DECLARE v_C INT;
        SET v_cCompte = LPAD(p_cCompte, 10, '0');
        FOR v_i IN 0..9 DO
            SET v_A = v_A + SUBSTRING(v_cCompte, v_i + 1, 1) * (POW(2, v_i) % 11);
        END FOR;
        SET v_C = 11 - (v_A % 11);
        IF v_C = 10 THEN
            SET v_C = 1;
        ELSEIF v_C = 11 THEN
            SET v_C = 0;
        END IF;
        RETURN v_C;
    END
    //
    DELIMITER ;
    
    Treballant amb BIGINT - TRUNCATE
    DELIMITER //
    CREATE OR REPLACE FUNCTION DigitControlCte(cCompte BIGINT)
        RETURNS INT
        DETERMINISTIC 
    BEGIN
        DECLARE I, vDigit INT;
        DECLARE vSuma INT DEFAULT 0;
        SET vDigit = 0;
        FOR I IN REVERSE 0..9 DO
            SET vDigit = cCompte % 10;
            SET cCompte = TRUNCATE(cCompte / 10, 0);
            SET vSuma = vSuma + (vDigit * (POW(2, i) % 11));
        END FOR;
        SET vDigit = 11 - MOD(vSuma, 11);
    
        IF vDigit = 10 THEN
            SET vDigit = 1;
        ELSEIF vDigit = 11 THEN
            SET vDigit = 0;
        END IF;
        -- RETORN DEL VALOR
        RETURN vDigit;
    END
    //
    DELIMITER ;
    SELECT DigitControlCte(0123456789); -- Ha de donar 1
    SELECT DigitControlCte(1234567890); -- Ha de donar 6
    

Cte2Char - Funció

  • Donat el codi de banc, oficina i número de compte, retorna un string formatant el compte de la següent forma: BBBB OOOO DD CCCCCCCCCC

    Càlcul dígits de Control (La Sexta)

    SET - CONCAT - CONVERT - LPAD - Crida a Funcions

    Exemple d'ús i execució
    SELECT Cte2Char(1234,1111,0123456789);
    -- Retorna '1234 1111 11 0123456789'
    SELECT Cte2Char(1234,2222,1234567890);
    -- Retorna '1234 2222 76 1234567890'
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE FUNCTION Cte2Char(cBanc INt, cOficina INT, cCompte BIGINT)
    RETURNS VARCHAR(25)
    DETERMINISTIC 
    BEGIN
        RETURN CONCAT(LPAD(cBanc,4,'0'), ' ', 
                    LPAD(cOficina,4,'0'), ' ', 
                    CONVERT(DigitControlBO(cBanc, cOficina), CHAR),
                    CONVERT(DigitControlCte(cCompte), CHAR), ' ', 
                    LPAD(cCompte, 10, '0')); 
    END
    // 
    DELIMITER ;
    SELECT Cte2Char(1234,1111,0123456789); -- Retorna '1234 1111 11 0123456789'
    SELECT Cte2Char(1234,2222,1234567890); -- Retorna '1234 2222 76 1234567890'
    

DarrersMoviments - Procediment

  • Donat un número d’oficina, un número de compte i una data ens mostra els moviments des d’aquella data en ordre de més nou a més antic. Cal mostar al final de cada línia el saldo després d'aquell moviment.

    SELECT - CURSOR ( FOR ) - SIGNAL

    Exemple d'ús i execució
    CALL darrersMoviments(2,1, CURRENT_DATE - INTERVAL 1 WEEK);
    Mov Data       Descripció           Import     Saldo
    004 2023-02-21 Reintegre            -125.00    275.00
    003 2023-02-20 Reintegre            -50.00     400.00
    CALL darrersMoviments(2,1, CURRENT_DATE - INTERVAL 1 MONTH);
    Mov Data       Descripció           Import     Saldo
    004 2023-02-21 Reintegre            -125.00    275.00
    003 2023-02-20 Reintegre            -50.00     400.00
    002 2023-02-14 Ingrés               350.00     450.00
    001 2023-02-09 Ingrés               100.00     100.00
    CALL darrersMoviments(2,2, CURRENT_DATE + INTERVAL 1 DAY);
    Mov Data       Descripció           Import     Saldo
    No hi ha moviments
    CALL darrersMoviments(1,2, CURRENT_DATE - INTERVAL 1 DAY);
    ERROR 1643 (02000): ERROR: Compte Inexistent
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE PROCEDURE DarrersMoviments(
        p_cOficina TYPE OF moviment.cOficina,
        p_cCompte  TYPE OF moviment.cCompte,
        p_Dia DATE)
    BEGIN
        DECLARE v_q INT DEFAULT 0;
        DECLARE v_saldoCompte TYPE OF compte.saldoCompte;
        DECLARE v_numeroMoviment TYPE OF moviment.numeroMoviment;
        DECLARE c1 CURSOR FOR 
            SELECT * FROM Moviment
            WHERE cOficina = p_cOficina AND cCompte = p_cCompte AND
                moment >= p_dia
            ORDER BY numeroMoviment DESC;
        -- Anem a buscar el Saldo actual, aprofitarem per mirar si existeix el Compte
        SELECT saldoCOmpte INTO v_saldoCompte FROM Compte
            WHERE cOficina = p_cOficina AND cCompte = p_cCompte;
        IF v_saldoCompte IS NULL THEN
            SIGNAL SQLSTATE '02000' SET MESSAGE_TEXT = 'ERROR: Compte Inexistent';
        END IF;
        SELECT CONCAT_WS(' ',
                        "Mov",
                        "Data      ", 
                        RPAD('Descripció', 35),
                        LPAD('Import',10),
                        LPAD('Saldo',10)) AS "";
        FOR r_moviment IN c1 DO
            SELECT CONCAT_WS(' ', 
                            LPAD(r_moviment.numeroMoviment, 3, '0'),
                            r_moviment.moment, 
                            RPAD(r_moviment.tipusMoviment, 35),
                            LPAD(FORMAT(r_moviment.import, 2),10),
                            LPAD(FORMAT(v_saldoCompte, 2),10)) AS "";
            SET v_saldoCompte = v_saldoCompte - r_moviment.import;
            SET v_q = v_q + 1;
        END FOR;
        IF v_q = 0 THEN
            SELECT "No hi ha moviments" AS "";
        END IF;
    END 
    //
    DELIMITER ;
    CALL darrersMoviments(2,1, CURRENT_DATE - INTERVAL 1 WEEK); -- Surt algun moviment
    CALL darrersMoviments(2,1, CURRENT_DATE - INTERVAL 1 MONTH); -- Surt algun moviment
    CALL darrersMoviments(2,2, CURRENT_DATE + INTERVAL 1 DAY); -- No hi ha moviments
    CALL darrersMoviments(1,2, CURRENT_DATE - INTERVAL 1 DAY); -- ERROR 1643 (02000): ERROR: Compte Inexistent
    

Cte2IBAN - Funció opcional

Problemes en el càlcul al treballar amb nombres amb tants dígits

  • Donat el codi de banc, oficina i número de compte, retorna un string amb el codi IBAN del compte de la següent forma:

    ESxx BBBB OOOO DD CCCCCCCCCC

  • Suposarem que TOTS els comptes són de l’estat espanyol (ES).

    Càlcul dígits de Control (La Sexta)

    El compte no es pot tractar tot sencer com un número ja que no podem representar números amb tants dígits BBBBOOOODDCCCCCCCCCC142800.

    Cal anar dividint, ens quedem el residu, de mica en mica, com si ho féssim a mà en paper, agafant els dígits des de l'esquerra de mica en mica fins agafar-los tots.

    Exemple d'ús i execució
    SELECT Cte2IBAN(1234,1111,0123456789); -- Ha de donar 53
    ES53 1234 1111 11 0123456789
    SELECT Cte2IBAN(1234,2222,1234567890); -- Ha de donar 90
    ES90 1234 2222 76 1234567890
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE FUNCTION Cte2IBAN(pcBanc INT, pcOficina INT, pcCompte BIGINT)
        RETURNS VARCHAR(30)
        DETERMINISTIC 
    BEGIN
        DECLARE v_treball BIGINT;
        DECLARE v_afegit BIGINT;
        DECLARE v_CompteChar VARCHAR(26);
        -- Generem el CHAR amb tots els dígits més 142800 del ES00 al final
        SET v_CompteChar = CONCAT(lpad(pcBanc,4,'0'),
                                  lpad(pcOficina,4,'0'),
                                  FORMAT(DigitControlBO(pcBanc, pcOficina),'0'),
                                  FORMAT(DigitControlCte(pcCompte),'0'),
                                  lpad(pcCompte,10,'0'),
                                  '142800');
        -- Agafem els primers 7 dígits. Podriem agafar-ne fins a 9 però millor quedar-nos curts
        SET v_treball = LEFT(v_CompteChar, 7);
        -- El dividim entre 97 i ens quedem el residu...
        SET v_treball = MOD(v_treball, 97);
        -- Ara hi anirem afegint la resta de dígits i en calularem el residu de dividir-ho per 97.
        -- Semblant a com dividiriem si ho fessim manualment en paper
        FOR v_i IN 8..LENGTH(v_CompteChar) DO -- Agafem els dígits que queden d'un en un
            -- Obtenim e dígit a afegir
            SET v_afegit = SUBSTR(v_CompteChar, v_i, 1); -- Podriem agafar-los de 5 en 5
            -- Afegim el dígit al final
            SET v_treball = v_treball * 10 + v_afegit;   -- Multipliquem per 10^numDigits que agafem
            -- Calculem el residu de dividir entre 97
            SET v_treball = MOD(v_treball, 97);
        END FOR;
        -- Calculem els dígits de control
        SET v_treball = 98 - v_treball;
        -- Ja tenim a v_treball els 2 dígits del IBAN;
        RETURN CONCAT_WS(' ',
                         CONCAT('ES', LPAD(v_treball,2,'0')), -- ha de tenir 2 dígits
                         lpad(pcBanc,4,'0'),
                         lpad(pcOficina,4,'0'),
                         CONCAT(FORMAT(DigitControlBO(pcBanc, pcOficina),'0'),
                                FORMAT(DigitControlCte(pcCompte),'0')),
                         lpad(pcCompte,10,'0'));
    END
    //
    DELIMITER ;
    SELECT Cte2IBAN(1234,1111,0123456789); -- Ha de donar 53
    SELECT Cte2IBAN(1234,2222,1234567890); -- Ha de donar 90
    

ComprovaOficines - Procediment

  • Mostra les oficines que no tenen el saldo correcte. Es comprova, oficina a oficina, que la suma dels imports dels seus comptes sigui el saldo de la oficina. Si no ho son, mostra les dades de la Oficina.

    Exemple d'execució
    CALL ComprovaOficines();
    START TRANSACTION;
    UPDATE oficina SET saldoOficina = saldoOficina - 10;
    CALL ComprovaOficines();
    1       Una     20.00   30.00
    2       Dues    440.00  450.00
    3       Tres    -10.00  NULL
    ROLLBACK;
    CALL ComprovaOficines();
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE PROCEDURE ComprovaOficines(
    )
    BEGIN
        DECLARE v_saldoOficinaEsperat TYPE OF oficina.saldoOficina;
        DECLARE c_oficina CURSOR FOR SELECT * FROM oficina;
    
        FOR r_oficina IN c_oficina DO
            SELECT SUM(saldoCompte) INTO v_saldoOficinaEsperat
                FROM compte WHERE cOficina = r_oficina.cOficina;
            IF IFNULL(r_oficina.saldoOficina, 0) <> IFNULL(v_saldoOficinaEsperat, 0) THEN
                SELECT r_oficina.cOficina, r_oficina.dOficina, r_oficina.saldoOficina, v_saldoOficinaEsperat;
            END IF;
        END FOR;
    END
    //
    DELIMITER ;
    CALL ComprovaOficines();
    START TRANSACTION;
    UPDATE oficina SET saldoOficina = saldoOficina - 10;
    CALL ComprovaOficines();
    ROLLBACK;
    CALL ComprovaOficines();
    

ComprovaMoviments - Procediment

  • Mostra els comptes que no tenen el saldo correcte. Es comprova, compte a compte, que la suma dels imports dels seus moviments sigui el saldo del compte. Si no ho son, mostra el número de compte.

    Exemple d'execució
    CALL ComprovaMoviments();
    START TRANSACTION;
    UPDATE compte SET saldoCompte = saldoCompte - 10;
    CALL ComprovaMoviments();
    1       1       20.00   30.00
    2       1       240.00  250.00
    2       2       190.00  200.00
    ROLLBACK;
    CALL ComprovaMoviments();
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE PROCEDURE ComprovaMoviments(
    )
    BEGIN
        DECLARE v_saldoCompteEsperat TYPE OF compte.saldoCompte;
        DECLARE c_compte CURSOR FOR SELECT * FROM compte;
    
        FOR r_compte IN c_compte DO
            SELECT SUM(import) INTO v_saldoCompteEsperat
                FROM moviment WHERE cOficina = r_compte.cOficina AND cCompte = r_compte.cCompte;
            IF IFNULL(r_compte.saldoCompte, 0) <> IFNULL(v_saldoCompteEsperat, 0) THEN
                SELECT r_compte.cOficina, r_compte.cCompte, r_compte.saldoCompte, v_saldoCompteEsperat;
            END IF;
        END FOR;
    END
    //
    DELIMITER ;
    CALL ComprovaMoviments();
    START TRANSACTION;
    UPDATE compte SET saldoCompte = saldoCompte - 10;
    CALL ComprovaMoviments();
    ROLLBACK;
    CALL ComprovaMoviments();
    

ComprovaSaldosNegatius - Procediment

  • Mostra els comptes que en algun moment han tingut el saldo en negatiu. Per comprovar-ho, cal anar, per cada compte, moviment a moviment calculant el saldo que tenia a cada moment. Si trobem un saldo negatiu, cal mostrar el compte.

    Exemple d'execució
    CALL ComprovaSaldosNegatius();
    START TRANSACTION;
    UPDATE moviment SET import = import * 2 WHERE import < 0;
    CALL ComprovaSaldosNegatius();
    1       1       3       -200.00 -140.00
    2       1       6       -250.00 -50.00
    ROLLBACK;
    CALL ComprovaSaldosNegatius();
    
    Possible solució
    DELIMITER //
    CREATE OR REPLACE PROCEDURE ComprovaSaldosNegatius(
    )
    BEGIN
        DECLARE v_saldo TYPE OF compte.saldoCompte;
        DECLARE c_compte CURSOR FOR SELECT * FROM compte;
        DECLARE c_moviment CURSOR (p_cOficina TYPE OF moviment.cOficina, p_cCompte TYPE OF moviment.cCompte)
            FOR SELECT * FROM moviment WHERE cOficina = p_cOficina AND cCompte = p_cCompte ORDER BY numeroMoviment;
    
        FOR r_compte IN c_compte DO
            SET v_saldo = 0;
            FOR r_moviment IN c_moviment(r_compte.cOficina, r_compte.cCompte) DO
                SET v_saldo = v_saldo + r_moviment.import;
                IF v_saldo < 0 THEN
                    SELECT r_compte.cOficina, r_compte.cCompte, r_moviment.numeroMoviment, r_moviment.import, v_saldo;
                END IF;
            END FOR;
        END FOR;
    END
    //
    DELIMITER ;
    CALL ComprovaSaldosNegatius();
    START TRANSACTION;
    UPDATE moviment SET import = import * 2 WHERE import < 0;
    CALL ComprovaSaldosNegatius();
    ROLLBACK;
    CALL ComprovaSaldosNegatius();