Bazy danych - Politechnika Gdańska
Transkrypt
Bazy danych - Politechnika Gdańska
Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” Instrukcja do laboratorium z przedmiotu: Bazy danych Laboratorium nr 4. Funkcje własne, procedury wyzwalane i przetwarzanie transakcyjne Opracował A. Bujnowski 2010-04-08 Projekt „Przygotowanie i realizacja kierunku inżynieria biomedyczna – studia międzywydziałowe” współfinansowany ze środków Unii Europejskiej w ramach Europejskiego Funduszu Społecznego. Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” 1. Cele laboratorium – zapoznanie się z metodami tworzenia własnych funkcji, technologią procedur wyzwalanych oraz przetwarzaniem transakcyjnym 2. Przykładowa baza danych: Jako przykład bazy danych posłuży nam przygotowana uprzednio struktura bazy dla wypożyczalni płyt DVD. Dla przypomnienia diagram relacyjny przykładowej bazy danych przedstawia poniższy rysunek. CREATE TABLE klient ( imie varchar(20) not null, nazwisko varchar(40) not null, nr_dowodu char(10), id_klienta serial primary key); CREATE TABLE gatunek( nazwa varchar(30) not null, id_gatunku serial PRIMARY KEY ); CREATE TABLE plyta( tytul varchar(40) not null, numer serial primary key, cena numeric(4,2), gatunek integer REFERENCES gatunek ON DELETE SET NULL ON UPDATE CASCADE); CREATE TABLE wypozyczenie( kto_wypozyczyl int not null REFERENCES klient ON DELETE RESTRICT ON UPDATE RESTRICT, co_wypozyczyl int not null REFERENCES plyta ON DELETE RESTRICT ON UPDATE CASCADE, data_wypozyczenia timestamp default noow(), 2 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” data_zwrotu timestamp, primary key(kto_wypozyczyl, co_wypozyczyl, d_wypozyczenia) ); CREATE TABLE jest_pracownikiem( rabat int, id_klienta int primary key references klient); Cel zajęć: Funkcje własne użytkownika. Funkcje agregacji Dla przypomnienia sprawdźmy działanie poniższych wywołań: SELECT SELECT SELECT SELECT 2+2; sin(1); log(numer) FROM plyta; sin(cena) FROM plyta; Ale istnieją także inne funkcje: SELECT SELECT SELECT SELECT SELECT count(*) FROM gatunek; count(imie) FROM klient; max(id_klienta) FROM klient; min(id_klienta) FROM klient; avg(id_klienta) FROM klient; Te funkcje w odróżnieniu od poprzednich zwracają pojedynczy wynik a operują na grupach krotek. Funkcje takie noszą miano funkcji agregacji, gdyż dokonują analizy na grupie krotek. Gdy wykonamy funkcję sin(id_klienta) to zwróci ona tyle wyników na ilu krotkach się ona wykonała, natomiast funkcje takie jak max(), min(), avg(), variance(), stdev() czy count() zwrócą pojedynczy wynik. Z tymi funkcjami związana jest dodatkowa klauzula zapytania SELECT : GROUP BY. GROUP BY powoduje przestawienie wyników w taki sposób, aby stanowiły one "przegrupowaną” tabelę względem kryterium. Na tak przegrupowanej tabeli możliwe jest wykonywanie funkcji agregacji i dalsza analiza wyników. Przykładowo – chcemy dokonać statystycznego zestawienia imion występujących w tabeli klient: SELECT imie, count(imie) FROM klient GROUP BY imie; Z klauzulą GOUP BY związana jest klauzula HAVING, która działa tak samo jak WHERE dla "normalnej” wersji SELECT: 3 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” SELECT imie, count(imie) FROM klient GROUP BY imie HAVING imie < 'J'; W ramach laboratorium pokazana została już własność polecenia SELECT polegająca na możliwości wywoływania funkcji lub operowania na danych (operacje arytmetyczne itp.). Pokazane zostały również własności agregujące (COUNT, MIN, MAX itp.) pozwalające na wykonywanie operacji zwracających pojedynczy wynik dla grup krotek. Przydatnym jednak może okazać się możliwość tworzenia własnych funkcji dających dodatkowe możliwości twórcy baz danych. Do tworzenia własnych funkcji służy polecenie CREATE FUNCTION. Zostało ono zdefiniowane w normie SQL:1999 i późniejszych. Składnia stosowana w PostgreSQL jest podobna, ale nie w pełni kompatybilna. Atrybuty nie są przenaszalne, jak również odmienne są języki programowania. Składnia polecenia CREATE FUNCTION podana jest poniżej. W celu uzyskania dokładniejszego opisu można skorzystać z materiałów podanych na wykładzie, bądź skorzystać z dokumentacji projektu na stronie www.postgresql.org CREATE [ OR REPLACE ] FUNCTION name ( [ [ argmode ] [ argname ] argtype [, ...] ] ) [ RETURNS rettype ] { LANGUAGE langname | IMMUTABLE | STABLE | VOLATILE | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER | COST execution_cost | ROWS result_rows | SET configuration_parameter { TO value | = value | FROM CURRENT } | AS 'definition' | AS 'obj_file', 'link_symbol' } ... [ WITH ( attribute [, ...] ) ] W poniższych przykładach będziemy korzystali z funkcji pisanych w dwóch dostępnych językach programowania : SQL oraz plpgsql. Pierwsza funkcja. Napiszmy funkcję, która zwróci nam pole koła: CREATE FUNCTION polekola( float) RETURNS float LANGUAGE 'plpgsql' AS ' BEGIN RETURN 3.1415*$1*$1; END; '; Gdy zachodzi konieczność poprawienia funkcji, wówczas nie trzeba jej kasować. Możliwe jest użycie polecenia 4 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” CREATE OR REPLACE FUNCTION polekola(float) …. Ważne jest aby przy takim użyciu polecenia redefiniowana funkcja nie zmieniała typu ani ilości danych wejściowych. Zatem, poprawiamy funkcję korzystając z gotowych funkcji np. zwracających stałe, aby otrzymać dokładniejszy wynik: CREATE OR REPLACE FUNCTION polekola( float) RETURNS float LANGUAGE 'plpgsql' AS ' DECLARE wpi float; BEGIN select pi() into wpi; RETURN wpi*$1*$1; END; '; Wywołanie tej funkcji odbywa się przez: SELECT polekola(5); Możliwe jest również wywołanie takiej funkcji dla całej kolumny w tabeli. Sprawdź jak zadziała wykonanie poniższego wywołania: SELECT polekola(id_klienta) FROM klient; Usuwanie funkcji: Do usunięcia funkcji używa się polecenia DROP FUNCTION podając nazwę funkcji i jej parametry wejściowe: DROP FUNCTION polekola(float); - nie rób tego !! pozwoli na usunięcie funkcji, która za chwilę zostanie utworzona. Paarametry wejściowe są konieczne ze względu na to że w PostgreSQL możliwe jest zdefiniowanie funkcji o tej samej nazwie działającej na róznych typach danych. Przypomina to bardzo przeciążanie operatorów. Zdefiniowaną funkcję można obejrzeć korzystając z zapytania: SELECT * from pg_proc WHERE proname='polekola'; SELECT prosrc FROM pg_proc WHERE proname='polekola'; Listę funkcji zdefiniowanych można obejrzec korzystając z \df Zadanie: 1. Stwórz funkcję o nazwie poletrojkata, która wyznaczy pole trójkąta ze wzoru 0.5*a*h, gdzie 5 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” a jest podstawą a h wysokością trójkąta. 2. Stwórz funkcją, która wyznaczy deltę z równania kwadratowego. Zademonstruj działanie funkcji prowadzącemu zajęcia. Bardziej wartościowe działanie funkcji. Załóżmy, że w bazie danych dotyczących naszej wypożyczalni mamy osobę (klienta) , jakiś obiekt wypożyczeń oraz tabelę wypożyczeń. Obiekt posiada cenę. Należy się zastanowić, czy klientowi naszej wypożyczalni przysługuje rabat, w związku z tym sprawdźmy sumę wartości jego wszystkich wypożyczeń, jeśli przekroczyła zadaną kwotę (np. 30pln) to zwróćmy wartość rabatu równą 10, jeśli nie to 0. CREATE FUNCTION retrabat(int) RETURNS int LANGUAGE plpgsql AS ' declare suma float; rabat int; begin select sum(cena) into suma from plyta,wypozyczenie where plyta.numer=wypozyczenie.co_wypozyczyl AND wypozyczenie.kto_wypozyczyl = $1; if suma > 30 then rabat = 10; else rabat = 0; end if; return rabat; end; '; Sprawdź działanie tej funkcji dla konkretnego klienta: SELECT retrabat(1); lub lepiej : SELECT retrabat((SELECT id_klienta from klient WHERE nazwisko like 'Kowalski')); Możesz też sprawdzić działanie tej funkcji dla wszystkich klientów w bazie: SELECT imie,nazwisko,retrabat(id_klienta) from klient; Zastanów się nad podobną funkcją, ale uzależniającą rabat od ilości wypożyczeń np. w ciągu 6 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” ostatnich 3 miesięcy. Zastanów się jak przekazać próg udzielania rabatu i okres sprawdzania wypożyczeń przez parametry wejściowe funkcji. Triggery TRIGGER, po polsku nazywany procedurą wyzwalaną, jest funkcją zdefiniowaną przez użytkownika wywoływaną, gdy pojawi sie jakieś wydarczenie (event) w tabeli. W Postgresie można wywoływać triggery przed/po wstawieniu, modyfikacji lub usunięciu rekordu. Triggery mogą być wykonywane raz dla całej instrukcji (statement level) lub po kolei dla każdego modyfikowanego rekordu (row level). Trigger definiuje się następującą składnią: CREATE TRIGGER nazwa (każdy trigger musi się jakoś nazywać) BEFORE albo AFTER (czy trigger ma być wykonany przed czy po wydarzeniu) INSERT albo UPDATE albo DELETE (których wydarzen trigger dotyczy, można połączyć kilka przez OR) ON tabela (trigger zawsze zakłada się na konkretną tabelę) FOR EACH ROW albo STATEMENT (czy trigger ma być wywołany raz na rekord, czy raz na instrukcję) EXECUTE PROCEDURE procedura(parametry); (co ma być wywołane jako obsługa triggera) Trigger usuwa się następującą składnią: DROP TRIGGER nazwa ON tabela; (usunięce konkretnego triggera z konkretnj tabeli) Tak więc, aby wywołać funkcję skasowano() po każdym skasowaniu rekordu z bazy rejestr należy wydać następujące polecenie: CREATE TRIGGER mojtrigger AFTER DELETE ON rejestr FOR EACH ROW EXECUTE PROCEDURE skasowano() ; Funkcja skasowano() musi zwracać typ TRIGGER. Sprawdź jak to działa: Stwórzmy funkcję skasowano() CREATE FUNCTION skasowano() RETURNS TRIGGER LANGUAGE plpgsql 7 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” AS ' BEGIN RAISE NOTICE ''Skasowano jeden rekord''; RETURN NULL; END; '; teraz stwórzmy trigger: CREATE TRIGGER tr_delkli AFTER DELETE ON klient FOR EACH ROW EXECUTE PROCEDURE skasowano(); i podobny do niego : CREATE FUNCTION skasowano1() RETURNS TRIGGER LANGUAGE plpgsql AS ' BEGIN RAISE NOTICE ''Skasowano rekordy''; RETURN NULL; END; '; CREATE TRIGGER tr_delkli1 AFTER DELETE ON klient FOR EACH statement EXECUTE PROCEDURE skasowano1(); Sprawdźmy jak to działa: delete from klient where id_klienta not in (select kto_wypozyczyl from wypozyczenie) and id_klienta not in (select id_klienta from jest_pracownikiem); Język PL/pgSQL może być używany do tworzenia procedur wyzwalanych. Najpierw należy stworzyć funkcję poleceniem CREATE FUNCTION deklarując ją jako funkcję bez argumentów wejściowych i zwracającą typ trigger. Zauważ, że funkcja musi być zdeklarowana bez argumentów wejściowych nawet jeżeli takowych wymaga. Wówczas argumenty przekazywane są do funkcji przez zmienną systemową o nazwie TG_ARGV. Kiedy wywoływana jest funkcja w PL/pgSQL jako TRIGGER kilka specjalnych zmiennych jest tworzonych. Są to: NEW Dana typu RECORD; zawiera nowy wiersz dla operacji typu INSERT/UPDATE w wyzwalaczach poziomu wierszowego. Przyjmuje NULL w wyzwalaczach typu statement-level. OLD Dana typu RECORD; zawiera stary wiersz dla operacji typu INSERT/UPDATE w wyzwalaczach poziomu wierszowego. Przyjmuje NULL w wyzwalaczach typu statement-level. 8 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” TG_NAME Nazwa name; nazwa triggera, który został aktualnie uruchomiony. TG_WHEN Zmienna tekstowa przechowująca wiadomość czy trigger został stworzony BEFORE czy AFTER zdarzenia. TG_LEVEL Zmienna tekstowa przechowująca wiadomość czy trigger został stworzony dla ROW czy STATEMENT. TG_OP Zmienna tekstowa przechowująca wiadomość czy trigger został stworzony dla INSERT, UPDATE czy DELETE. TG_RELID ID objektu, który wywołał trigger. TG_RELNAME Nazwa tabeli, która wywołała trigger. TG_NARGS Typ danych integer; Liczba argumentów podanych do procedury funkcji triggera. TG_ARGV[] Macierz danych tekstowych; argumenty podawane do procedury funkcji triggera. Funkcja deklarowana na potrzeby wyzwalaczy musi zwrócić NULL albo wartość typu rekord/wiersz mającą dokładnie strukturę tabeli, dla której trigger zadziałał. Triggery wyzwalane dla wierszy działające przed akcją (BEFORE) mogą zwracać null aby odwołać akcję, która została dla danego wiersza odpalona (np. wszystkie następujące wyzwalacze i działania typu INSERT/UPDATE/DELETE nie zostaną wykonane dla tego wiersza. Jeśli trigger zwróci wartość nie-null wszystko co następuje po tej funkcji zostanie dla tego wiersza wykonane. 9 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” Zwracając wartość wiersza różną od wartości oryginalnej dla NEW podmienia wartość tego wiersza w przypadku INSERT lub UPDATE ale nie ma bezpośredniego wpływu na DELETE. Aby podmienić zawartość wiersza możliwa jest podmiana pojedynczych wartości tego wiersza bezpośrednio lub stworzenie kompletnie nowego wiersza (struktury) typu NEW. Wartość zwracana dla wywołania typu STATEMENT, lub AFTER dla wywołania typu ROW jest zawsze ignorowana – może być wartością typu NULL. Tym niemniej dla każdego z tych typów triggerów akcja może zostać przerwana poprzez wymuszenie komunikatu błędu (raise error). Efekty uboczne w fazie BEFORE Triggery w fazie BEFORE mogą mieć dwa różne efekty uboczne: • • W przypadku wszystkich wydarzeń - jeżeli procedura triggera zwróci wartość NULL, to dana czynność ma zostać anulowana i dalsze triggery związane z tą czynnością nie zostaną wywołane. Często mówi się, że taki trigger wetuje jakąś operację. W przypadku wydarzeń UPDATE i INSERT procedura triggera może zwrócić nową zawartość rekordu i wtedy ta właśnie wartość (a nie ta wynikająca z parametrów instrukcji UPDATE lub INSERT INTO) znajdzie się w bazie. Macierz właściwości wydarzenie/f BEFORE aza • • INSERT • • • • UPDATE • AFTER Możliwe jest zawetowanie wstawienia. Wstawiane wartości dostępne są w tablicy NEW. Modyfikacja zawartości tablicy NEW powoduje wstawienie zmodyfikowanych danych. W przypadku braku weta funkcja powinna zwrócić NEW. Możliwe jest zawetowanie uaktualnienia. Aktualne wartości dostępne są w tablicy OLD a nowe w tablicy NEW. Modyfikacja zawartości tablicy NEW powoduje wstawienie zmodyfikowanych danych. 10 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski • • • • Dane są już fizycznie wstawione do tabeli. Wstawione dane znajdują się w tablicy NEW. Dane są już fizycznie zmodyfikowane w tabeli. Poprzednie wartości dostępne są w tablicy OLD a aktualne w tablicy NEW. Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” • W przypadku braku weta funkcja powinna zwrócić NEW. • Możliwe jest jedynie zawetowanie usunięcia. Nie ma możliwości uaktualnienia zamiast skasowania. Pola kasowanego rekordu dostępne są w tablicy OLD • DELETE • • • Rekord jest już fizycznie skasowany Pola skasowanego rekordu dostępne są w tablicy OLD Transakcje Transakcja pozwala na zgrupowanie pewnego ciągu zdarzeń w bazie danych w jedną nierozerwalną całość. Każda transakcja spełnia podstawowe reguły określane z j. Angielskiego ACID (patrz wykład) – niepodzielna, spójna, izolowana , trwała (atomic, consistent,isolated , durable). Oznacza to, że każda transakcja jako blok danych albo zostanie wykonana w całości, albo wcale – niezależnie od innych transakcji, system bazodanowy po transakcji będzie spójny i dane po zakończeniu transakcji będą trwałe – nawet w przypadku awarii systemu zarządzania bazą danych... Każdą transakcję zaczynamy słowem BEGIN, po czym następuje dowolnie długi ciąg zdarzeń w SQLu i kończymy utrwalając zmiany słowem COMMIT, bądź je odrzucając poprzez ROLLBACK. Sprawdź jak to działa: połącz się z bazą danych w dwóch równoległych oknach (wcześniej stwórz klienta, który nie ma nic wypożyczone i zapamiętaj id) wpisuj co następuje [o1: - w oknie pierwszym; o2: - w oknie drugim] : o1: BEGIN; o1: SELECT * FROM klient ; o2: SELECT * FROM klient ; o1: DELETE FROM klient WHERE id_klienta=12; – tu wpisz zapamiętane ID o2: SELECT * FROM klient ; o1: SELECT * FROM klient ; o1: ROLLBACK; o1: SELECT * FROM klient; o2: SELECT * FROM klient ; a teraz spróbuj tego samego z zatwierdzeniem zmian; o1: o1: o2: o1: BEGIN; SELECT * FROM klient ; SELECT * FROM klient ; DELETE FROM klient WHERE id_klienta=12;; 11 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” o2: o1: o1: o1: o2: SELECT * SELECT * COMMIT; SELECT * SELECT * FROM klient ; FROM klient ; FROM klient; FROM klient ; Blokady tabel przy transakcjach: o1: o1: o2: o1: begin; lock table klient; select * from klient; commit; Zakleszczenia Przykład: o1: o2: o1: o2: o1: o2: begin; begin; update update update update klient klient klient klient set set set set imie='Jan' where id_klienta=3; imie = 'Ewa' where id_klienta=4; imie='Jan' where id_klienta=4; imie = 'Ewa' where id_klienta=3; Komentarze: Komentarze można umieszczać pisząc kod w SQL w dowolnym edytorze tekstowym. Taki komentarz nie zostanie zinterpretowany przez SZBD, - pozostanie jedynie w pliku tekstowym. Komentarze tego typu oznacza się dwoma znakami '-' i taki komentarz trwa do końca linii. np. : -- to jest komentrarz do SQL Istnieje również inna metoda zapisu dodatkowej informacji w projekcie. Dzięki możliwości przechowywania tekstu w bazie można opisać i przechować w ten sposób komentarz do dowolnego obiektu. Tego typu komentarze tworzy się jako: COMMENT ON { TABLE object_name | COLUMN table_name.column_name | AGGREGATE agg_name (agg_type) | 12 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” CONSTRAINT constraint_name ON table_name | DATABASE object_name | DOMAIN object_name | FUNCTION func_name (arg1_type, arg2_type, ...) | INDEX object_name | OPERATOR op (leftoperand_type, rightoperand_type) | RULE rule_name ON table_name | SCHEMA object_name | SEQUENCE object_name | TRIGGER trigger_name ON table_name | TYPE object_name | VIEW object_name } IS 'text' czyli np.: COMMENT ON TABLE klient IS ' To jest tabela przechowująca listę klientów naszego banku'; Sprawdźmy jak to działa: po wpisaniu komentarza spróbuj zrobić backup bazy danych. Załółżmy, że baza nazywa się XXXX psql XXXX COMMENT on ... (jaK WYŻEJ) \q pg_dump XXXX > mojabazaXXXX.sql dropdb XXXX – teraz możesz obejrzeć plik z wynikami twojej bazy. Załóż bazę spowrotem: createdb XXXX psql XXXX -f mojabazaXXXX.sql sprawdź tabele (zwłaszcza klient) Zadania do realizacji: 1. Stwórz funkcję i wyzwalacz, która przy próbie wypożyczenia płyty nie pozwoli wypożyczyć nowej płyty użytkownikowi, który ma 5 lub więcej niezwróconych pozycji. 2. Opisz wszystkie tabele stosownymi komentarzami 3. Stwórz funkcję, która pozwoli sprawdzić i naliczyć karę użytkownikowi za zbyt długie przetrzymywanie płyty. Jeżeli przekracza ono 2 tygodnie to za każdy dzień dolicz 10 pln. 4. Wewnątrz transakcji dodaj nowego użytkownika i informację, że jest on pracownikiem, przewidź rabat 50%. 5. Stwórz w bazie danych nową tabelę o nazwie kary – zastanów się jak ją powiązać z istniejącą strukturą i jak przechowywane będą dane. 13 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski