PROCEDURY W PostgreSQL mamy do dyspozycji nie tylko funkcje wbudowane, ale również możemy tworzyć własne. Są one zapisywane w tabeli systemowej pg_proc. \df Aby wyświetlić wszystkie funkcje z argumentami używamy \dd Aby wyświetlić komentarze na temat określonej funkcji używamy Tworzenie własnych funkcji przydaje się, gdy dane zapytanie lub jakieś obliczenia chcemy wykonywać wielokrotnie. Tworzenie funkcji CREATE [OR REPLACE] FUNCTION nazwa_funkcji ([typ_funkcji]) RETURNS typ_wyniku_funkcji AS definicja_funkcji LANGUAGE nazwa_języka ; Definicja_funkcji pojedynczy ciąg znaków, może obejmować kilka wierszy (zamknięty w apostrofy). Jeżeli w funkcji znajdują się apostrofy należy je wyróżnić (poprzedzić je dodatkowym apostrofem). Nazwa_języka dowolny ładowalny język proceduralny obsługiwany przez PostgreSQL. (np. SQL lub PL/pgSQL). Aby można było użyć dowolnie wybranego języka należy włączyć jego obsługę w PostgreSQL. Aby wyświetlić listę języków wyświetlamy zawartość tabeli systemowej pg_language SELECT * FROM pg_language; Przy pierwszym wywołaniu funkcji jest ona dopiero kompilowana do postaci wykonywalnej, a następnie wykonywana. Przykładowa funkcja dodająca 1 do podanej liczby (w języku PL/pgSQL) CREATE FUNCTION dodaj_jeden(int4) RETURNS int4 AS ' RETURN $1+1; 'LANGUAGE 'plpgsql Aby wyświetlić jej działanie dla liczby 3 używamy SELECT dodaj_jeden(3) AS wynik; 19 listopada 2012 Strona 1
Ale jeśli podamy argument innego typu : SELECT dodaj_jeden(3,1) AS wynik; Przeciążanie funkcji CREATE FUNCTION dodaj_jeden (float8) RETURNS float8 AS ' RETURN $1+1; 'LANGUAGE 'plpgsql Wykonując teraz SELECT dodaj_jeden(3.1) AS wynik; Otrzymamy Aby wyświetlić kod źródłowy funkcji tworzymy zapytanie do tabeli (pg_proc) w której zapisywane są procedury SELECT prosrc FROM pg_proc WHERE proname='dodaj_jeden Otrzymamy dwie procedury o tej samej nazwie 19 listopada 2012 Strona 2
Usuwanie funkcji DROP FUNCTION nazwa_funkcji (typ_funkcji); DROP FUNCTION dodaj_jeden(int4); W języku SQL Aby wykorzystać język SQL do tworzenia funkcji należy język procedury określić jako 'SQL'. Funkcje te przyjmują parametry, do których odwołujemy się poprzez $1, $2 itd. Nie istnieją instrukcje sterujące. Wartością zwracaną przez funkcję SQL są dane zwracane przez ostatnio wykonaną instrukcję SQL (zwykle SELECT). Jego zaletą jest brak ładowania procedury obsługi innych języków. Możliwe jest zwrócenie przez funkcję więcej niż jednego wiersza jeśli zadeklarujemy typ zwracanej wartości jako setof. CREATE FUNCTION klienci(text) RETURNS setof customer AS' SELECT * FROM customer WHERE town=$1; 'LANGUAGE 'SQL Uruchamiamy funkcję SELECT klienci('bingham'); I otrzymujemy 19 listopada 2012 Strona 3
Aby wyświetlić tylko nazwisko (określoną kolumnę) SELECT lname(klienci('bingham')) AS customer; Inny sposób: CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int) AS 'SELECT $1 + $2, $1 * $2' LANGUAGE SQL; SELECT * FROM sum_n_product (11,42); CREATE TYPE sum_prod AS (sum int, product int); CREATE FUNCTION sum_n_product1 (int, int) RETURNS sum_prod AS 'SELECT $1 + $2, $1 * $2' LANGUAGE SQL; SELECT * FROM sum_n_product1(11,42); W języku PL/pgSQL Jest to język blokowo-strukturalny (podobny do C lub Pascal), udostepniający dekaracje zmiennych i posiadający zakresy bloków. W języku PL/pqSQL nie jest ważna wielkość liter dla słów kluczowych. Komentarze w pojedynczym wierszu poprzedzone są dwoma minusami (--), a komentarze blokowe mają postać taką jak w C (/* */). Bloki mają strukturę: [etykieta] [ deklaracje] Instrukcje; [RETURN wyrażenie_zwracane]; Etykieta jak również deklaracje są opcjonalne dla każdego z bloków. Zmienne deklarowane w bloku są widoczne tylko w tym bloku, zmienne wewnętrzne (o tej samej nazwie co w bloku zewnętrznym) przesłaniają zmienne zewnętrzne. Instrukcja RETURN jest obowiązkowa dla najbardziej zewnętrznego bloku funkcji. Wartość zwracana musi być zgodna z zadeklarowanym typem wyniku. 19 listopada 2012 Strona 4
Aliasy tworzymy w celu polepszenia czytelności kodu używając : Nazwa_aliasu ALIAS FOR $n; Gdzie n jest numerem parametru wejściowego funkcji. Deklarujemy zmienne jako Nazwa_zmiennej [CONSTANT] typ [NOT NULL][:=wartość_zmiennej] Istnieje możliwość deklarowania typów zgodnych z typami innych elementów bazy danych (uniezależnia nas to od problemów w przypadku późniejszej zmiany typu jakiegoś elementu w bazie co mogłoby wywołać błędy w funkcjach wykorzystujących te elementy). Dla zmiennej prostej: Nazwa_zmiennej nazwa_elementu_bazy_danych%type Spowoduje utworzenie zmiennej o nazwie nazwa_zmiennej, której typ będzie identyczny jak typ elementu bazy danych o nazwie nazwa_elementu_bazy_danych. Dla zmiennej złożonej: Nazwa_zmiennej_złożonej nazwa_tabeli%rowtype Spowoduje utworzenie zmiennej o nazwie nazwa_zmiennej_złożonej posiadającej pola odpowiadające kolumnom tabeli o nazwie nazwa_tabeli. Instrukcje warunkowe: IF Instrukcje takie mogą być zagnieżdżane jak w innych językach programowania IF wyrażenie THEN instrukcje [ELSE instrukcje] END IF; CASE liczba par WHEN/THEN jest dowolna. Całość zwraca wartość wyrażenie w części THEN dla odpowiedniego wyrażenia WHEN, lub dla ELSE. CASE WHEN wyrażenie THEN wyrażenie WHEN wyrażenie THEN wyrażenie... ELSE wyrażenie 19 listopada 2012 Strona 5
Pętle: WHILE [etykieta] -- opcjonalna WHILE wyrażenie LOOP instrukcje END LOOP; FOR wykonywane dla każdej wartości z zakresu (od.. do), dla pętli tworzona jest nowa zmienna o naziw nazwa_zmiennej, która jest zwiększana (lub zmniejszana jeśli użyjemy opcjonalnego słowa REVERSE) o 1 po każdym wykonaniu pętli. FOR nazwa_zmiennej IN[REVERSE] od.. do LOOP instrukcje END LOOP; Funkcja bez argumentów CREATE OR REPLACE FUNCTION ex01() RETURNS text RETURN `HELLO!`; SELECT ex01(); Funkcja z argumentami CREATE OR REPLACE FUNCTION ex03(real) RETURNS real RETURN $1*$1; SELECT ex03(2.7); 19 listopada 2012 Strona 6
CREATE FUNCTION zmienne1(int4) RETURNS float8 AS ' n integer := 1; my_pi CONSTANT float8 = pi(); r ALIAS FOR $1; RETURN my_pi * r * r; ' LANGUAGE 'plpgsql SELECT zmienne1(1); ZAD1 Utworzyć funkcję zwracającą długość wektora o podanych współrzędnych x i y (parametry wejściowe i wartość zwracana są typu real) Aliasy parametrów wejściowych: CREATE OR REPLACE FUNCTION ex04(real) RETURNS real param ALIAS FOR $1; RETURN param*param; Typ zmiennej może być określony jako typ skopiowany z atrybutu tabeli. Składnia: zmienna tabela.atrybut%type CREATE TABLE ex05t ( id serial, name varchar(10), hiredate date ); CREATE OR REPLACE FUNCTION ex05(ex05t.name%type, ex05t.hiredate%type) RETURNS text RETURN $1 '' '' $2; SELECT ex05('julia','1009-01-01'); 19 listopada 2012 Strona 7
Typ wierszowy PostgreSQL pozwala deklarować tzw. zmienne złożone, odpowiadające całym wierszom w określonej tabeli. Składnia: nazwa tabela%rowtype; ROWTYPE jest słowem kluczowym, tabela jest nazwą tabeli. Wynikiem tej deklaracji jest zmienna zawierająca pola, po jednym dla każdej kolumny tabeli, na podstawie której powstała. Aby skorzystać z pól, należy zastosować składnię: zmienna.pole. Innym rodzajem typu złożonego jest RECORD. Jest on podobny do ROWTYPE ale nie opiera się on na konkretnej tabeli. Typ ten jest wypełniany wartościami w czasie wykonania. CREATE TABLE ex06t (id serial,name varchar(10), hiredate date); INSERT INTO ex06t VALUES (DEFAULT,'KOWALSKI','2007-01-01'); CREATE OR REPLACE FUNCTION ex06(ex06t) RETURNS text x ex06t; x.name := $1.name; x.hiredate=$1.hiredate; RETURN x.name '' zatrudniony od '' x.hiredate; SELECT ex06(t.*) FROM ex06t t; Kwerendy nie zwracające rezultatu Kwerendy, które nie zwracają żadnego rezultatu, mogą być wywoływane z wnętrza funkcji bezpośrednio. CREATE TABLE ex07t (id serial,name varchar(10),hiredate date); CREATE OR REPLACE FUNCTION ex07(varchar,date) RETURNS void INSERT INTO ex07t VALUES(DEFAULT,$1,$2); RETURN; SELECT ex07('kowalski','2007-01-01'); SELECT * FROM ex07t; 19 listopada 2012 Strona 8
Kwerenda zwracająca 1 wiersz Rezultat kwerendy zwracającej 1 wiersz może być przypisany do zmiennej wierszowej, rekordowej lub listy wartości. Składnia: SELECT... INTO zmienna/lista FROM... Zmienna wierszowa i lista wartości muszą dokładnie odpowiadać zwracanemu wierszowi. Zmienna rekordowa zostanie dopasowana automatycznie CREATE TABLE ex08t (id serial,name varchar(10),hiredate date); INSERT INTO ex08t VALUES(DEFAULT,'KOWALSKI','2007-04-01'); CREATE OR REPLACE FUNCTION ex08(varchar) RETURNS text val RECORD; SELECT * INTO val FROM ex08t WHERE name=$1; RETURN val.name '' zatrudniony '' val.hiredate; SELECT ex08('kowalski'); Status rezultatu kwerendy Aby określić status rezultatu kwerendy można skorzystać z kilku metod: Polecenie GET DIAGNOSTICS Składnia: GET DIAGNOSTICS zmienna = klucz; Kluczem może być: ROW_COUNT liczba wierszy RESULT_OID OID ostatnio wstawionego wiersza CREATE TABLE ex09t (id serial,name varchar(10),hiredate date); CREATE OR REPLACE FUNCTION ex09(varchar,date) RETURNS integer err integer; INSERT INTO ex09t VALUES(DEFAULT,$1,$2); GET DIAGNOSTICS err := ROW_COUNT; RETURN err; SELECT ex09('kowalski','2007-01-01'); SELECT * FROM ex09t; 19 listopada 2012 Strona 9
Instrukcje warunkowe CREATE TABLE ex10t (id serial,name varchar(10),sal numeric); CREATE OR REPLACE FUNCTION ex10(varchar,numeric) RETURNS integer err integer; IF $2>=0 THEN INSERT INTO ex10t VALUES(DEFAULT,$1,$2); END IF; GET DIAGNOSTICS err := ROW_COUNT; RETURN err; SELECT ex10('kowalski',1200); SELECT ex10('karwowski',-500); SELECT * FROM ex10t; Pętle CREATE OR REPLACE FUNCTION ex11(integer,integer) RETURNS SETOF INTEGER FOR i IN $1.. $2 LOOP RETURN NEXT 2^i; END LOOP; RETURN; SELECT * FROM ex11(1,10); CREATE TABLE integers (i integer); CREATE OR REPLACE FUNCTION ex12() RETURNS VOID FOR i IN 1.. 100 LOOP INSERT INTO integers VALUES (i); END LOOP; RETURN; SELECT ex12(); SELECT * FROM integers; 19 listopada 2012 Strona 10
Pętla FOR z kwerendą CREATE OR REPLACE FUNCTION ex14() RETURNS SETOF real arg RECORD; FOR arg IN SELECT * FROM integers LOOP RETURN NEXT arg.i/10.0; END LOOP; RETURN; SELECT * from ex14(); Informacje o produktach, których zaczyna brakować w magazynie (w tabeli stock) DROP TABLE reorders; CREATE TABLE reorders(item_id integer, message text); CREATE OR REPLACE FUNCTION reorders(int4) RETURNS integer AS ' min_stock ALIAS FOR $1; reorder_item integer; reorder_count integer; stock_row stock%rowtype; msg text; SELECT count(*) INTO reorder_count FROM stock WHERE quantity <=min_stock; FOR stock_row IN SELECT * FROM stock WHERE quantity<=min_stock LOOP item_row item%rowtype; SELECT * INTO item_row FROM item WHERE item_id=stock_row.item_id; msg:= '' Zamowic '' item_row.description; INSERT INTO reorders VALUES (stock_row.item_id,msg); END LOOP; RETURN reorder_count; 'LANGUAGE 'plpgsql Wywołując procedurę dla produktów, których jest w magazynie nie więcej niż 3 sztuki 19 listopada 2012 Strona 11
SELECT reorders(3); Otrzymujemy, że należy zamówić 3 produkty Po wyświetleniu zawartości tabeli reorders mamy: ZAD2 Utworzyć funkcję tablicującą wartości funkcji y = A*x^2+B*x+C dla wartości A,B,C podanych przez użytkownika i x w zakresie od 1 do 100. Rezultat ma być zwracany w postaci dwukolumnowej (jako x oraz wartość funkcji dla danego x). ZAD3 Utworzyć funkcję tablicującą wartości temperatury w skali Fahrenheita dla temperatur w Celsjusza w przedziale 1 do 100 co 0.1 stopnia ZAD4 Utworzyć bezargumentową funkcję liczba_studentow zwracającą liczbę studentów w tabeli szkola.studenci ZAD5 Utworzyć funkcję liczba_studentow_1 zwracającą liczbę studentów w podanej jako argument grupie ZAD6 Utworzyć funkcję srednia zwracającą średnią ocen studenta o nazwisku podanym jako argument funkcji ZAD7 Login studenta składa się z połączenia jego nazwiska i pierwszej litery imienia (wszystko małymi literami). Napisać funkcję generującą login na podstawie wiersza tabeli szkola.studenci. 19 listopada 2012 Strona 12
ZAD8 Utworzyć funkcję zwracającą (jako typ złożony zdefiniowany przez użytkownika) nazwisko studenta oraz średnią ocen studenta którego numer indeksu przekazany został jako argument funkcji ZAD9 Utworzyć funkcję zwracającą listę ocen studentów. Lista ma zawierać imię i nazwisko studenta, nazwę przedmiotu i ocenę. Wariant 1: Wykorzystać wcześniej zdefiniowany widok Wariant 2: Wykorzystać własny typ zwracanych danych 19 listopada 2012 Strona 13