DAM - DAW - MP 0484 Bases de Dades - NF4
En una base de dades, una de les sentències més utilitzades és SELECT. Després d'una sentència de selecció de files el que necessitem és anar recorrent i tractant cada una d'aquestes files. L'eina que ens permet fer-ho és el CURSOR.
Un cursor és una estructura que ens permet recórrer tots els registres de forma seqüencial i processar cada una de les files retornades per una SELECT.
Declaració de cursors
Els Cursors podem declarar-los, de forma explícita, amb la clàusula DECLARE CURSOR.
Per recórrer tots els registres que retorna el CURSOR, el més senzill és utilitzar l'estrutura ...
FOR pels Cursors
La sentència FOR ens permet recórrer tots els registres d'una consulta SELECT de la forma més snezilla.
Els cursors poden ser explícits o implícits i encara se simplifica més el seu ús.
Cursor explícit
[begin_label:]
FOR record_name IN cursor_name [(cursor_actual_parameter_list)]
DO statement_list
END FOR [ end_label ];
Cursor implícit
[begin_label:]
FOR record_name IN ( select_statement )
DO statement_list
END FOR [ end_label ];
Començarem per la forma més simple.
FOR amb cursor implícit
BEGIN NOT ATOMIC
FOR r_jobs IN (SELECT * FROM empresa.jobs) DO
SELECT r_jobs.job_title;
END FOR;
END;
En aquest cas no cal declarar res de res.
S'executa la SELECT i tenim una variable de tipus ROW TYPE anomenada r_jobs on a cada iteració hi haurà un nou registre de la SELECT.
No hem hagut de declarar la variable r_jobs.
Per accedir a cadascun dels camps de la fila ho farem seguint el següent exemple: r_jobs.job_title.
FOR amb cursor explícit
BEGIN NOT ATOMIC
DECLARE c_jobs CURSOR FOR SELECT * FROM empresa.jobs;
FOR r_jobs IN c_jobs DO
SELECT r_jobs.job_title;
END FOR;
END;
En aquest cas només hem hagut de declarar el cursor c_jobs que conté la consulta.
L'avantatge dels cursors és que podem passar-li paràmetres.
No hem hagut de declarar la variable r_jobs.
Exemple amb iteracions i cursors anidats
Anem a veure un exemple on utilitzarem dos cursors. EL primer ens permetrà anar mostrants els departaments. Amb el segon, accedirem als empleats del departament. Farem l'exemple amb CURSORS explícits i implícits.
DELIMITER //
BEGIN NOT ATOMIC
DECLARE v_quants INTEGER;
DECLARE c_departments CURSOR FOR SELECT * FROM departments ORDER BY department_name;
DECLARE c_employees CURSOR (p_department_id TYPE OF departments.department_id)
FOR (SELECT * FROM employees WHERE department_id = p_department_id
ORDER BY LAST_NAME, FIRST_NAME, SALARY);
FOR r_departments IN c_departments DO
SET v_quants = 0;
SELECT r_departments.department_name;
SELECT REPEAT('=', LENGTH(r_departments.department_name));
FOR r_employees IN c_employees(r_departments.department_id) DO
SELECT CONCAT(' ', r_employees.last_name, ', ', r_employees.first_name, ' - ', r_employees.salary);
SET v_quants = v_quants + 1;
END FOR;
SELECT CONCAT(v_quants, ' empleat', CASE v_quants WHEN 1 THEN '' ELSE 's' END);
SELECT '';
END FOR;
END;
//
DELIMITER ;
DELIMITER //
BEGIN NOT ATOMIC
DECLARE v_quants INTEGER;
FOR r_departments IN (SELECT * FROM departments ORDER BY department_name) DO
SET v_quants = 0;
SELECT r_departments.department_name;
SELECT REPEAT('=', LENGTH(r_departments.department_name));
FOR r_employees IN (SELECT * FROM employees
WHERE department_id = r_departments.department_id ORDER BY LAST_NAME, FIRST_NAME, SALARY)
DO
SELECT CONCAT(' ', r_employees.last_name, ', ', r_employees.first_name, ' - ', r_employees.salary);
SET v_quants = v_quants + 1;
END FOR;
SELECT CONCAT(v_quants, ' empleat', CASE v_quants WHEN 1 THEN '' ELSE 's' END);
SELECT '';
END FOR;
END;
//
DELIMITER ;
SELECT sense FOR
En el cas de no utilitzar FOR, caldrà:
-
declarar les variables sobre els que recuperarem les dades de cada fila, que podran ser variables simples o compostes,
-
declarar el cursor amb la consulta
SELECT, -
obrir el cursor,
-
anar agafant les files una a una amb la instrucció
FETCH, -
controlar quan s’arriba al final de les files,
-
tancar el cursor
Per controlar el final de les files només podrem fer-ho deixant que la sentència FETCH doni error, capturant aquest error, DECLARE CONTINUE HANDLER, i executant una comanda que ens permeti saber que s’ha arribat al final de les files, IF v_fi THEN.
Veurem l’exemple i més endavant explicarem el tractament dels errors amb els handlers.
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET v_fi = TRUE;
Exemple de SELECT sense FOR
No ho utilitzarem.
BEGIN NOT ATOMIC
DECLARE v_fi BOOLEAN DEFAULT FALSE; -- Controla final files
DECLARE r_jobs ROW TYPE OF empresa.jobs; -- On rebem les files
DECLARE c_jobs CURSOR FOR SELECT * FROM empresa.jobs; -- SELECT
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET v_fi = TRUE; -- Captura l'error ERROR 1329 (02000):
-- No data - zero rows fetched, selected, or processed
OPEN c_jobs;
FETCH c_jobs INTO r_jobs;
label_jobs: LOOP
IF v_fi THEN
LEAVE label_jobs;
END IF;
SELECT r_jobs.job_title; -- Tractem les dades
FETCH c_jobs INTO r_jobs;
END LOOP label_jobs;
CLOSE c_jobs;
END;
Fixeu-vos que, per fer la mateixa feina, sense el FOR, necessitem moltes més
instruccions i estructures.
Cal dir que l’estructura FOR és força nova i abans calia utilitzar la fórmula que acabem de veure.