Cursores, Triggers, Indices, Transacciones, Vistas Gestión y Modelación de Datos Cursores CREATE OR REPLACE FUNCTION incSalario (INTEGER) RETURNS TEXT AS ' DECLARE curEmp CURSOR FOR SELECT nombres, apellidos, salario FROM empleado ORDER BY apellidos, nombres FOR UPDATE; nombres TEXT; apellidos TEXT; sal INTEGER; BEGIN OPEN curEmp; <<ciclo>> LOOP FETCH curEmp INTO nombres, apellidos, sal; IF NOT FOUND THEN EXIT ciclo; END IF; UPDATE empleado SET salario = salario + $1 WHERE CURRENT OF curEmp; $1 = $1 + 5000; END LOOP; CLOSE curEmp; RETURN '' ''; END; ' LANGUAGE 'plpgsql'; Cursores ● Declaración name REFCURSOR; name [ [ NO ] SCROLL ] CURSOR [ ( arguments ) ] FOR query [FOR UPDATE]; Ejemplos: DECLARE curs1 REFCURSOR; curs2 CURSOR FOR SELECT * FROM empleado; curs3 CURSOR (sal integer) IS SELECT * FROM empleado WHERE salario >= sal; Cursores ● Abrir OPEN name [ ( argument_values ) ];; OPEN name [ [ NO ] SCROLL ] FOR query [FOR UPDATE]; OPEN name [ [ NO ] SCROLL ] FOR EXECUTE query_string [ USING expression [, ... ] ]; Ejemplos: OPEN curs1 FOR SELECT * FROM empleado; OPEN curs3 (1000000); OPEN curs4 FOR EXECUTE ’SELECT * FROM ’ || quote_ident(tabname) || ’ WHERE col1 = $1’ USING keyvalue; Cursores ● Usar FETCH [ direction { FROM | IN } ] cursor INTO target; direction: NEXT, PRIOR, FIRST, LAST, ABSOLUTE count, RELATIVE count, FORWARD, or BACKWARD Verificar resultado con FOUND Ejemplos: FETCH curs1; FETCH curs3 RELATIVE -2; Cursores ● Reposicionar: MOVE [ direction { FROM | IN } ] cursor; Verificar resultado con FOUND ● Actualizar/Eliminar UPDATE table SET ... WHERE CURRENT OF cursor; DELETE FROM table WHERE CURRENT OF cursor; ● Cerrar: CLOSE cursor; Ejercicio ● ● Agregar a la tabla empleados un campo “SucesorDpto” de tipo entero Escribir una función que asigne el SucesorDpto, este valor se calcula así: cod_dpto * 100 + consecutivo ● El consecutivo se asigna a cada empleado del departamento en orden alfabético, iniciando en 1. Cursores ● Recorrido [ <<label>> ] FOR recordvar IN bound_cursorvar [ ( argument_values ) ] LOOP statements END LOOP [ label ]; CREATE OR REPLACE FUNCTION incSalario2 (INTEGER) RETURNS TEXT AS ' DECLARE curEmp CURSOR FOR SELECT nombres, apellidos, salario FROM empleado ORDER BY apellidos, nombres FOR UPDATE; BEGIN <<ciclo>> FOR emp IN curEmp LOOP UPDATE empleado SET salario = salario + $1 WHERE CURRENT OF curEmp; RAISE INFO '' % % '', emp.apellidos, emp.salario; $1 = $1 + 5000; END LOOP; RETURN ''OK''; END; ' LANGUAGE 'plpgsql'; Triggers ● ● Trigger: operaciones que se realizan cuando un evento específico ocurre en la BD Trigger Function: función invocada por un trigger. Debe retornar un valor de tipo de dato opaque CREATE TRIGGER name { BEFORE | AFTER } { event [ OR event ... ] } ON table FOR EACH { ROW | STATEMENT } EXECUTE PROCEDURE function ( arguments ) ● Eventos: INSERT, UPDATE, DELETE Triggers - Ejemplo CREATE TRIGGER ValidaSueldo BEFORE UPDATE ON empleado FOR EACH ROW EXECUTE PROCEDURE ValidaSueldo () CREATE OR REPLACE FUNCTION ValidaSueldo () RETURNS opaque AS ' DECLARE sueldoJefe int; BEGIN SELECT INTO sueldoJefe jefe.salario FROM empleado as jefe INNER JOIN empleado ON (jefe.cedula = empleado.ced_jefe) WHERE empleado.cedula = NEW.cedula; IF NOT FOUND THEN RAISE EXCEPTION ''No hay jefe''; END IF; IF sueldoJefe <= NEW.salario THEN RAISE EXCEPTION ''Salario del Jefe es Menor''; END IF; RETURN NEW; END; ' LANGUAGE 'plpgsql'; Trigger Function ● Variables: ● NEW: en INSERT y UPDATE ● OLD: en UPDATE Y DELETE ● TG_WHEN: 'BEFORE', 'AFTER' ● TG_OP: 'INSERT', 'UPDATE','DELETE' ● TG_RELNAME: nombre de la relación ● TG_NARGS: número de argumentos ● TG_ARGV[]: argumentos Ejercicio ● Crear un trigger para la actualización y creación del empleado, que valide que el jefe existe en la base de datos antes de hacer la operación. Ejemplo de uso de tablas temporales CREATE OR REPLACE FUNCTION ejemploTmp () RETURNS char AS ' DECLARE curEmp REFCURSOR; emp empleado%ROWTYPE; BEGIN DROP TABLE IF EXISTS empTmp; CREATE TEMP TABLE empTmp AS SELECT * FROM empleado; OPEN curEmp FOR SELECT * FROM empTmp WHERE salario > 1000000; <<ciclo>> LOOP FETCH curEmp INTO emp; IF NOT FOUND THEN EXIT ciclo; END IF; PERFORM actualizaSal (emp.cedula, emp.salario, emp.ced_jefe); END LOOP; CLOSE curEmp; RETURN ''''; END; ' LANGUAGE 'plpgsql'; Ejemplo de uso de tablas temporales CREATE OR REPLACE FUNCTION actualizaSal (integer, integer, integer) RETURNS char AS ' DECLARE ced ALIAS FOR $1; sal ALIAS FOR $2; cedJefe ALIAS FOR $3; newSal integer; BEGIN IF sal > (SELECT salario FROM empleado WHERE cedula = cedJefe)/2 THEN newSal = TRUNC(sal * 1.05); UPDATE empTmp SET salario = newSal WHERE cedula = ced; END IF; RETURN ''''; END; ' LANGUAGE 'plpgsql'; Ejemplo de uso de tablas temporales Las tablas temporales existen mientras no se borren o hasta que se cierre la sesión. En este ejemplo, después de ejecutar la función ud. Puede consultar datos de empTmp, mientras permanezca en la misma sesión. Indices ● Una forma de mejorar el tiempo de respuesta de las consultas a la base de datos, pero también crean una sobrecarga para el sistema en las actualizaciones. CREATE INDEX name ON table (column [, ...]); CREATE UNIQUE INDEX name ON table (column [, ...]); Ejemplo: CREATE INDEX nombres ON empleado(Apellidos, Nombres) Vistas ● ● Darle un nombre a una consulta de la base de datos, para referirse a ella como a una tabla. Útiles para encapsular la estructura de las tablas, para que las interfaces permanezcan consistentes a pesar de la evolución de la aplicación. CREATE VIEW name AS query; ● Ejemplo: CREATE VIEW nombres AS (SELECT cedula, nombres || ' ' || apellidos as NombreCompleto FROM empleado); Transacciones ● Une varios pasos en una sola operación que se ejecuta “Todo-o-nada” BEGIN; Statements...; COMMIT; ● Propiedades ACID ● ROLLBACK; ● SAVEPOINT savepointname; ● ROLLBACK TO savepointname: Transacciones Ejemplo: en una base de datos al registrar las ventas, se actualiza también el valor de la comisión del vendedor que la realizó, y la cantidad en inventario del producto. Ejercicios ● ● ● ● Cree un indice sobre la tabla departamento. Cree una vista “EmpleadosEspaña”, que seleccione los datos de los empleados del departamento “Regional España”. Usando la vista “EmpleadosEspaña” seleccione la cédula de los jefes de los empleados de esta regional. Cree una función que cambie el jefe de un departamento. Esta función actualiza la ced_jefe de departamento, y actualiza ced_jefe de empleado, para todos los empleados de ese departamento. Use una transacción para asegurar la consistencia de la base de datos.