Omet navegació

6.2 - Execució de funcions

Considerarem alguns casos diferents, depenent del comportament de la funció a la qual cridem.

Funció que torna un únic resultat

És el cas més habitual. I el més senzill de tractar, ja que no hi haurà cap diferència amb el que hem vist fins el moment: executem una sentència que torna valors, per tant executeQuery(), i en la sentència SQL cridem la funció. El següent exemple crida a la funció MEDIANA(), feta en el Tema 7, pregunta 6, i que calculava la mediana de les altures de les poblacions. El resultat és 230:

import java.sql.*;

public class Exemple_6_2_1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        String url = "jdbc:postgresql://89.36.214.106:5432/geo_1cfsy_0000x";
        String usuari = "geo_1cfsy_0000x";
        String password = "geo_1cfsy_0000x";

        Connection con = DriverManager.getConnection(url, usuari, password);
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery("SELECT MEDIANA()");
        while (rs.next()) {
            System.out.println("" + rs.getDouble(1));
        }
        st.close();
        con.close();
    }
}

Funció que torna més d'un resultat

També és molt fàcil d'utilitzar, ja que no canvia res en el tractament des de Java.

El que és més complicat és muntar la funció en PL/pgSQL ja que ara no torna un únic resultat. La manera és dir que torne un SETOF és a dir, un conjunt de resultats. També canviarà la manera de fer el RETURN, ja que ara no torna un únic valor. La manera d'arreglar-ho en PL/pgSQL és amb RETURN NEXT, és a dir, que torne el següent valor.

En aquesta funció creada sobre la Base de Dades geo_1cfsy_0000x (podeu substituir pel vostre dni, i així crear la funció en la vostra Base de Dades), tornem el nom de les comarques d'una província passada com a paràmetre:

CREATE OR REPLACE FUNCTION public.torna_comarques(varchar) RETURNS SETOF varchar AS $cos$
declare
	f record;
begin
	for f in select * from COMARQUES where PROVINCIA = $1 loop 
		return next f.nom_c;
	end loop;
end;
$cos$ LANGUAGE plpgsql;

Com comentàvem, la utilització és exactament igual que abans:

import java.sql.*;

public class Exemple_6_2_2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        String url = "jdbc:postgresql://89.36.214.106:5432/geo_1cfsy_0000x";
        String usuari = "geo_1cfsy_0000x";
        String password = "geo_1cfsy_0000x";

        Connection con = DriverManager.getConnection(url, usuari, password);
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery("select * from torna_comarques('Castelló')");
        while (rs.next()) {
            System.out.println(rs.getString(1));
        }
        st.close();
        con.close();
    }
}

Si intentem traure un resultat més complicat, amb més d'una columna, se'ns complica un poc. Ara el SETOF no pot ser d'un tipus senzill (en l'exemple anterior era un SETOF VARCHAR). Podem posar SETOF RECORD.

Només tindrem el problema que quan cridem la funció, hem d'especificar quina estructura té el que es torna.

Mirem un exemple, en el qual ens creem una funció que torna el nom de les comarques i total de població de la comarca, d'una província introduïda com a paràmetre:

CREATE OR REPLACE FUNCTION torna_comarques2(varchar) RETURNS SETOF RECORD AS $cos$
declare
	f record;
begin
	for f in select COMARQUES.nom_c, SUM(poblacio) 
					from COMARQUES inner join POBLACIONS using(nom_c) 
					where PROVINCIA = $1 
					group by 1
					loop 
		return next f;
	end loop;
end;
$cos$ LANGUAGE plpgsql;

I ara per a accedir des de Java, com comentàvem, especifiquem l'estructura del que es torna:

import java.sql.*;

public class Exemple_6_2_2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        String url = "jdbc:postgresql://89.36.214.106:5432/geo_1cfsy_0000x";
        String usuari = "geo_1cfsy_0000x";
        String password = "geo_1cfsy_0000x";

        Connection con = DriverManager.getConnection(url, usuari, password);
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery("select * from torna_comarques2('Castelló') as (nom varchar, num_habs numeric)");
        while (rs.next()) {
            System.out.println(rs.getString(1) + ": " + rs.getInt(2) + " habitants");
        }
        st.close();
        con.close();
    }
}

Funció que torna per l'eixida estàndar

Si la nostra funció és de les que torna per l'eixida estàndar (RAISE NOTICE), el que hem de fer és agafar tots els missatges que ens venen amb rs.getStatement().getWarnings().

Ho farem sobre una funció ja creada en el tema 7 pregunta 6, anomenada pobl_com_2(varchar), que tornava amb RAISE NOTICE totes les poblacions de la comarca passada com a paràmetre, en ordre ascendent. Us pose ací la funció, però segurament ja la tindreu creada:

CREATE FUNCTION pobl_com_2(text) RETURNS void AS $cos$
DECLARE
    f RECORD;
BEGIN
    FOR f IN SELECT nom FROM POBLACIONS
                WHERE nom_c = $1
                ORDER BY nom
    LOOP
        RAISE NOTICE '%',f.nom;
    END LOOP;
END;
$cos$ LANGUAGE plpgsql;

I ara el programa en Java

import java.sql.*;

public class Exemple_6_2_3 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        String url = "jdbc:postgresql://89.36.214.106:5432/geo_1cfsy_0000x";
        String usuari = "geo_1cfsy_0000x";
        String password = "geo_1cfsy_0000x";

        Connection con = DriverManager.getConnection(url, usuari, password);
        Statement st = con.createStatement();
        ResultSet rs = st.executeQuery("select pobl_com_2('Plana Alta')");
        for (Throwable m : rs.getStatement().getWarnings()) {
            System.out.println(m.getMessage());
        }
        st.close();
        con.close();
    }
}