Salta el contingut

DAM - DAW - MP 0484 Bases de Dades - NF5

Funcions

Les funcions es creen amb la comanda CREATE FUNCTION. A diferència dels procediments, retornen un valor d'un tipus especificat.

CREATE FUNCTION permet donar un nom a la funció i definir una sèrie de paràmetres que podrem utilitzar dins el codi com si fossin variables locals.

Aquests paràmetres, en el cas de les funcions, només poden ser d’entrada (IN).

Una funció sempre ha de retornar un valor de sortida.

CREATE FUNCTION

Sintaxis de CREATE FUNCTION.

CREATE [OR REPLACE]
    [DEFINER = {user | CURRENT_USER | role | CURRENT_ROLE }]
    [AGGREGATE] FUNCTION [IF NOT EXISTS]
    func_name([func_parameter[,...]])
    RETURNS type
    [characteristic ...]
    RETURN func_body

func_parameter:
    [ IN | OUT | INOUT | IN OUT ]  param_name type

Characteristic:
    LANGUAGE SQL
    | [NOT] DETERMINISTIC
    | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
    | SQL SECURITY { DEFINER | INVOKER }
    | COMMENT 'string'

CREATE FUNCTION crea una funció emmagatzemada.

Quan s’invoca des d’una altra base, diferent a la que pertany, s’executa un USE baseDeDades implícit.

Les funcions les podem utilitzar en sentències SQL de qualsevol tipus: DML, assignacions, com part d’una expressió, etc.

Les funcions només admeten paràmetres d’entrada.

En cas de tenir paràmetres de sortida o de entrada i sortida només es podrà utilitzar la funció en sentències SET, a partir de la versió 10.8.

DETERMINISTIC

Una funció és DETERMINISTIC quan, donats uns paràmetres d'entrada determinats, sempre produeix el mateix resultat.

Si el resultat es pot veure afectat per dades emmagatzemades, variables del servidor, nombres aleatoris o qualsevol valor que no s'ha passat explícitament, aleshores la funció és NOT DETERMINISTIC.

A més, una funció és NOT DETERMINISTIC si utilitza funcions no deterministes com NOW() o CURRENT_TIMESTAMP().

Exemple DETERMINISTIC

DELIMITER //
CREATE OR REPLACE
    FUNCTION Maxim(pUn INT, pDos INT)
    RETURNS INT DETERMINISTIC
BEGIN
    DECLARE v_major INT;
    IF pUn > pDos THEN
       SET v_major = pUn;
    ELSE
        SET v_major = pDos;
    END IF;
    RETURN v_major;
END//
DELIMITER ;
SELECT Maxim(23,45);
SELECT Maxim(45,23);

Exemple NOT DETERMINISTIC

DELIMITER //
CREATE OR REPLACE
    FUNCTION QuantsEmpleatsDepartament
    (p_dept_id TYPE OF departments.department_id)
    RETURNS INT
    NOT DETERMINISTIC
BEGIN
    DECLARE v_quants INT;
    SELECT COUNT(*) INTO v_quants
        FROM employees
        WHERE DEPARTMENT_ID = p_dept_id;
    RETURN v_quants;
END//
DELIMITER ;
SELECT QuantsEmpleatsDepartament(50);
SELECT QuantsEmpleatsDepartament(190);
SELECT QuantsEmpleatsDepartament(100);
-- la darrera SELECT dona 0 tot i no existir el departament

Exemple amb SIGNAL

DELIMITER //
CREATE OR REPLACE
    FUNCTION QuantsEmpleatsDepartament
    (p_dept_id TYPE OF departments.department_id)
    RETURNS INT READS SQL DATA NOT DETERMINISTIC
BEGIN
    DECLARE v_quants INT;
    DECLARE v_dept_id TYPE OF departments.department_id;
    DECLARE v_missatge VARCHAR(50);
    SELECT department_id, COUNT(employee_id) INTO v_dept_id, v_quants
        FROM employees RIGHT JOIN departments USING (department_id)
        WHERE department_id = p_dept_id
        GROUP BY department_id;
    IF v_dept_id IS NULL THEN
        SET v_missatge = CONCAT_WS(' ','Departament',p_dept_id,'no trobat');
        SIGNAL SQLSTATE '02000' SET MESSAGE_TEXT= v_missatge;
    END IF;
    RETURN v_quants;
END//
DELIMITER ;
SELECT QuantsEmpleatsDepartament(50);
SELECT QuantsEmpleatsDepartament(190);
SELECT QuantsEmpleatsDepartament(100); -- dona l'error del SIGNAL

ALTER FUNCTION

ALTER FUNCTION s'utilitza per a modificar alguna de les característiques d’un procediment. No permet modificar ni els paràmetres ni el codi. Per a fer aquests canvis tenim la comanda CREATE OR REPLACE FUNCTION.

ALTER FUNCTION Maxim
    CONTAINS SQL
    SQL SECURITY INVOKER 
    COMMENT 'Retorna el valor més gran de dos passats';

Diccionari de dades

select * from information_schema.routines 
where routine_name = 'QuantsEmpleatsDepartament';
Name Value
SPECIFIC_NAME QuantsEmpleatsDepartament
ROUTINE_CATALOG def
ROUTINE_SCHEMA empresa
ROUTINE_NAME QuantsEmpleatsDepartament
ROUTINE_TYPE FUNCTION
DATA_TYPE int
NUMERIC_PRECISION 10
NUMERIC_SCALE 0
DTD_IDENTIFIER int(11)
*** ***
ROUTINE_BODY SQL
*** ***
PARAMETER_STYLE SQL
IS_DETERMINISTIC YES
SQL_DATA_ACCESS READS SQL DATA
*** ***
SECURITY_TYPE DEFINER
CREATED AAAA-MM-DD HH:mm :SS
LAST_ALTERED AAAA-MM-DD HH:mm :SS
*** ***
DEFINER usuari@localhost
*** ***