Kursory Każde zapytanie SQL umieszczone w programie PL/SQL
Transkrypt
Kursory Każde zapytanie SQL umieszczone w programie PL/SQL
Kursory Każde zapytanie SQL umieszczone w programie PL/SQL jest wykonywane w tzw. obszarze roboczym lub inaczej obszarze kontekstu. PL/SQL wykorzystuje ten obszar do przechowywania danych otrzymanych w wyniku zapytania oraz do przechowywania dodatkowych informacji dotyczących stanu wykonywanego zapytania, tzw. atrybutów. Kursor (ang. cursor) jest konstruktorem PL/SQL umożliwiającym: • nadanie nazwy temu obszarowi, • dostęp do niego, • pobranie z niego danych, • kontrolę procesu przetwarzania danych. Wyróżniamy dwa rodzaje kursorów: • jawne (ang. explicit) - stosowane w zapytaniach wyznaczających wiele krotek i w tzw. pętlach z kursorem (ang. Cursor For Loop), • niejawne (ang. implicit) - stosowane w pozostałych poleceniach SQL (tj., insert, update, delete) i poleceniu select wyznaczającym jedną krotkę. Deklarowanie kursora. CURSOR nazwa [ ( param1 typ1 [,param2 typ2] ... ) ] [RETURN typ zwracany] IS zapytanie SQL; Jeśli w klauzuli WHERE w deklaracji kursora występują odwołania do zmiennych PL/SQL (o takich zmiennych mówimy, że są zmiennymi dowiązanymi), to zmienne te muszą być widoczne w miejscu deklaracji kursora, czyli muszą być zadeklarowane przed deklaracją kursora. Kursory jawne Przed użyciem kursora jawnego należy go najpierw zadeklarować poleceniem declare: Przykład 1 declare /* deklaracja kursora */ cursor osoba_kursor is select nazwisko, nazwa from pracownik, zespol where pracownik.id_zesp = zespol.id_zesp and zespol.nazwa = 'BAZY DANYCH'; Otwarcie kursora open nazwa_kursora; Po otwarciu kursora następuje: – Sprawdzenie wartości zmiennych dowiązanych, – Określenie zbioru wyników na podstawie wartości zmiennych dowiązanych, – Ustawienie wskaźnika zbioru wynikowego na pierwszym wierszu. Przykład 2 declare n zespol.nazwa%type; cursor osoba_kursor is select nazwisko, nazwa from pracownik, zespol where pracownik.id_zesp = zespol.id_zesp and zespol.nazwa = n; begin n = 'BAZY DANYCH'; open osoba_kursor; …... …... … W momencie otwarcia kursora zmienna n ma wartość 'BAZY DANYCH' i ta wartość zostaje użyta w zapytaniu. Zmiana wartości zmiennej po wykonaniu instrukcji OPEN nie zmieni zbioru wynikowego zapytania. To zjawisko nazywamy spójnością odczytu. Aby w zapytaniu uwzględniona została nowa wartość zmiennej należy kursor zamknąć i ponownie go otworzyć. Zbiór wynikowy lub zbiór wierszy jest określany w momencie otwierania kursora. Wówczas ustalany jest również wskaźnik pokazujący wiersz, który ma być pobrany przez kursor jako następny. UWAGA: Nie można otworzyć wcześniej otwartego kursora. Pobranie danych z kursora fetch nazwa_kursora into lista_zmiennych; lub fetch nazwa_kursora into rekord_PL/SQL; UWAGA: lista_zmiennych oraz pola rekordu muszą być zgodne pod względem typu z listą wyboru zapytania. Przykład 3 declare cursor pracownik_kursor is select nazwisko, placa_pod, id_zesp from pracownik; osoba_nazwisko pracownik.nazwisko%TYPE; osoba_płaca pracownik.placa_pod%TYPE; osoba_id_zesp pracownik.id_zesp%TYPE; pracownik_dane pracownik_kursor%ROWTYPE; begin open pracownik_kursor; fetch pracownik_kursor into osoba_nazwisko, osoba_płaca, osoba_id_zesp; fetch pracownik_kursor into pracownik_dane; …………… Polecenie fetch pobiera pojedynczy wiersz, a wskaźnik zbioru wynikowego zwiększa wartość odpowiednio dla następnego wiersza. W celu pobrania większej liczby wierszy jest konieczne wielokrotne użycie polecenia fetch (np. w pętli). Zamknięcie kursora close nazwa_kursora; Po pobraniu wszystkich wierszy ze zbioru wynikowego należy zamknąć kursor. Do mechanizmu PL/SQL wysyłane są informacje, że przetwarzanie kursora zakończyło się i zasoby z tym związane (pamięć przydzielona dla zbioru wynikowego oraz tymczasowa przestrzeń wykorzystywana do wyznaczania tego zbioru) mogą być zwolnione. UWAGA: Nieprawidłowe jest zamykanie zamkniętego już kursora. Przykład 4 declare cursor pracownik_kursor is select nazwisko, placa_pod, id_zesp from pracownik; osoba_nazwisko pracownik.nazwisko%TYPE; osoba_płaca pracownik.placa_pod%TYPE; osoba_id_zesp pracownik.id_zesp%TYPE; pracownik_dane pracownik_kursor%ROWTYPE; begin open pracownik_kursor; fetch pracownik_kursor into osoba_nazwisko, osoba_płaca, osoba_id_zesp; fetch pracownik_kursor into pracownik_dane; close pracownik_kursor; end; / Do kursora można przekazać parametry, jego deklaracja jest wtedy następująca: declare cursor nazwa_kursora (param_1 typ_1, ..., param_n typ_n) is … param_1 typ_1, ..., param_n typ_n oznaczają parametry kursora oraz ich typy. Typy parametrów są takie same jak typy zmiennych. Przykład 5 DECLARE -- Deklarujemy kursor z dwoma parametrami CURSOR osoba (n pracownik.nazwisko%TYPE, e pracownik.etat%TYPE) IS SELECT nazwisko, etat FROM pracownik p WHERE UPPER(p.nazwisko)= UPPER(n) AND UPPER(p.etat) = UPPER(e) ORDER BY 1 DESC; zm_osoba osoba%ROWTYPE; BEGIN -- Otwarcie kursora. Wielkość liter w łańcuchu bez znaczenia. OPEN osoba('maleja','ADIUNKT'); LOOP FETCH osoba INTO zm_osoba; EXIT WHEN osoba%NOTFOUND; DBMS_OUTPUT.PUT_LINE zm_osoba.nazwisko||' '|| END LOOP; CLOSE osoba; END; / (osoba%ROWCOUNT ||'. zm_osoba.etat); '|| Z każdym kursorem są związane cztery atrybuty, w których jest przechowywana informacja o przebiegu operacji wykonywanych przez kursor. %NOTFOUND: przyjmuje wartość true jeśli ostatnie polecenie fetch nie wyznaczyło krotki spełniającej warunek selekcji; w przeciwnym przypadku przyjmuje wartość false. W poniższym przykładzie, jeśli kursor nie wyznaczy krotki, atrybut osoba_kursor%NOTFOUND przyjmie wartość true i nastąpi wyjście z pętli. loop fetch osoba_kursor into osoba_nazwisko, osoba_nazwa; exit when osoba_kursor%NOTFOUND; ……………… end loop; %FOUND: przyjmuje wartość true jeśli ostatnie polecenie fetch wyznaczyło krotkę spełniającą warunek selekcji; w przeciwnym przypadku przyjmuje wartość false; %ROWCOUNT: wyznacza liczbę krotek pobranych poleceniem fetch; loop fetch osoba_kursor into osoba_nazwisko, osoba_nazwa; if osoba_kursor%ROWCOUNT > 10 then ………………… /* odczytano ponad 10 krotek */ end loop; %ISOPEN: przyjmuje wartość true jeśli kursor jest otwarty, w przeciwnym przypadku przyjmuje wartość false: if osoba_kursor%ISOPEN then /* pobierz krotkę */ fetch osoba_kursor into osoba_nazwisko, osoba_nazwa; else /*otwórz kursor */ open osoba_kursor; end if; Przykład 6 Kursor jawny – pobieranie danych z więcej niż jednego wiersza w pętli FOR. DECLARE CURSOR c_emp IS SELECT * FROM zespol ORDER BY nazwa; BEGIN FOR uv_emp IN c_emp LOOP DBMS_OUTPUT.PUT_LINE(uv_emp.id_zesp||' '||uv_emp.nazwa||' '|| uv_emp.adres); END LOOP; END; / Komentarz: Zmienna użyta w pętli FOR nie musi być zadeklarowana. PL/SQL niejawnie deklaruje zmienną, której zasięg jest ograniczony do instrukcji pętli. Można więc pominąć jawne deklarowanie zmiennej rekordowej. Można też POMINĄĆ instrukcje OPEN, CLOSE i FETCH. Przykład 7 Kursor jawny – pomijanie deklaracji kursora. BEGIN FOR uv_emp IN ( SELECT * FROM zespol ORDER BY nazwa) LOOP DBMS_OUTPUT.PUT_LINE(uv_emp.id_zesp||' '||uv_emp.nazwa||' '|| uv_emp.adres); END LOOP; END; / Komentarz: W tej konstrukcji można pominąć deklarację kursora oraz instrukcje OPEN, FETCH i CLOSE. UWAGA: Zwięzłość kodu nie zawsze jest pożądana. Przykładowo nie można ponownie wykorzystywać kursora. Czasami lepiej mieć wszystkie instrukcje SELECT w sekcji DECLARE – przy dłuższych programach ułatwia to jego analizowanie. Zad.1 Zdefiniować kursor, który zawierać będzie nazwę etatu, nazwę zespołu oraz nazwisko pracowników zatrudnionych po dacie zatrudnienia pracownika o nazwisku LECH. Do wypisania zawartości kursora użyć instrukcji OPEN, FETCH i CLOSE. Zad.2 Zdefiniować kursor, który zawierać będzie roczną średnią płacę pracowników w poszczególnych grupach etatowych. Wypisać komunikaty DUZO jeśli kwota odczytana z kursora jest większa od 10000 lub MALO w przeciwnym przypadku. (Użyj pętli FOR.) Zad.3 Zdefiniuj kursor zawierający numery tych pracowników, których nazwiska zawierają na dowolnym miejscu literę podaną jako parametr kursora, wyniki uporządkuj rosnąco. Zastosuj pętlę FOR do wypisania informacji na ekranie. Parametrem kursora ma być litera podana przez użytkownika. Zad.4 Napisz blok, w którym: – zdefiniuj kursor zawierający niepowtarzające się etaty, na których zatrudnieni są pracownicy pracujący w zespole o nazwie podanej przez użytkownika; – wypisz na ekranie sumę płac podstawowych pracowników zatrudnionych na etatach występujących w kursorze; – W przypadku gdy kursor jest pusty, wypisz komunikat ZESPOL NIE ZATRUDNIA PRACOWNIKOW. Zastosuj pętlę FOR. Zad.5 Zdefiniuj blok, w którym: – zdefiniuj kursor k1 zawierający nie powtarzającą się datę zatrudnienia „najmłodszego” pracownika na etacie podanym jako parametr przez użytkownika; – zdefiniuj kursor k2 zawierający numery pracowników mających datę zatrudnienia późniejszą od daty podanej jako parametr. – wypisz na ekranie zawartość kursora k2 dla daty znajdującej się w kursorze k1. (Wykorzystaj polecenia OPEN-FETCH-CLOSE.) Kursory niejawne • nie są deklarowane przez programistę, • polecenia open, fetch, close są wykonywane niejawnie przez system, • atrybuty przechowujące informacje o przebiegu wykonywanych operacji: SQL%NOTFOUND, SQL%FOUND, SQL%ROWCOUNT, SQL%ISOPEN. Ich znaczenie jest takie samo, jak odpowiednich atrybutów kursora jawnego. Na przykład, wynik usunięcia pracownika o nazwisku Mały może być sprawdzony przez atrybut kursora niejawnego SQL%NOTFOUND w następujący sposób: delete from pracownik where nazwisko like 'Maleja'; if SQL%NOTFOUND then ... /* nie ma osoby o takim nazwisku */ SQL%ROWCOUNT - wyznacza liczbę krotek, na których wykonano ostatnie polecenie insert, update, delete, np. update pracownicy set placa_pod = placa_pod * 1.2 where id_zesp = 10; if SQL%ROWCOUNT > 10 then ... /* uaktualniono więcej niż 10 wierszy */ elsif SQL%ROWCOUNT = 0 then ... /* żaden wiersz nie spełnia warunku instrukcji update można np. wstawić nowy wiersz*/ end if; SZBD automatycznie zamyka niejawny kursor po wykonaniu polecenia, co oznacza, że wartością atrybutu SQL%ISOPEN jest zawsze false. Przykład 8 Kursor niejawny – pobieranie danych z jednego wiersza. DECLARE uv_nazwisko VARCHAR2(25); uv_etat VARCHAR2(25); BEGIN SELECT nazwisko, etat INTO uv_nazwisko, uv_etat FROM pracownik WHERE numer = 1100; DBMS_OUTPUT.PUT_LINE('nazwisko: '||uv_nazwisko||', etat: '||uv_etat); END; / Przykład 9 DECLARE uv_nazwisko pracownik.nazwisko%TYPE; uv_etat pracownik.etat%TYPE; BEGIN SELECT nazwisko, etat INTO uv_nazwisko, uv_etat FROM pracownik WHERE numer = 1100; DBMS_OUTPUT.PUT_LINE('nazwisko: '||uv_nazwisko||', etat: '||uv_etat); END; / Przykład 10 DECLARE uv_nazwisko pracownik.nazwisko%TYPE; uv_etat pracownik.etat%TYPE; ile PLS_INTEGER; BEGIN SELECT nazwisko, etat INTO uv_nazwisko, uv_etat FROM pracownik WHERE numer = 1100; DBMS_OUTPUT.PUT_LINE('nazwisko: '||uv_nazwisko||', etat: '||uv_etat); EXCEPTION /* występuje tylko dla instrukcji SELECT */ WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('Zapytanie nie zwrocilo danych.'); WHEN TOO_MANY_ROWS THEN SELECT COUNT(*) INTO ile FROM pracownik WHERE numer = 1100; DBMS_OUTPUT.PUT_LINE('Zapytanie zwrocilo ' || ile || ' rekordow.'); END; / Kursory z klauzulą FOR UPDATE instrukcji SELECT W celu modyfikowania wierszy pobieranych przez kursor język PL/SQL dostarcza klauzuli FOR UPDATE w deklaracji kursora oraz klauzuli WHERE CURRENT OF w instrukcji UPDATE lub DELETE. Klauzula FOR UPDATE występuje jako ostatnia (za ORDER BY) klauzula w poleceniu SELECT. SELECT … FROM … FOR UPDATE [OF odnosnik_do_kolumny] [NOWAIT], gdzie odnosnik_do_kolumny jest kolumną tabeli, na której jest wykonywane zapytanie. Może to być również lista kolumn. Przykład 11 DECLARE - - Ten kursor wylistuje dwie kolumny dla klauzuli UPDATE CURSOR wszyscy_pracownicy IS SELECT * FROM pracownik FOR UPDATE OF nazwisko, placa_podst; - - Ten kursor nie wylistuje żadnej kolumny CURSOR duza_placa IS SELECT numer, placa_podst FROM pracownik WHERE placa_podst > 2500 FOR UPDATE; Zmienne kursora Przestawione powyżej przykłady kursorów jawnych są przykładami kursorów statycznych - kursor jest związany z jedną instrukcją SQL i ta instrukcja jest znana w momencie kompilacji bloku. Zmienna kursora może być skojarzona z różnymi instrukcjami w czasie wykonywania kodu. Przed użyciem zmienna kursora musi być zadeklarowana. W czasie wykonania dla zmiennej kursora musi być przydzielona pamięć (typ FER). Następnie kursor jest otwierany, są z niego pobierane dane i jest zamykany (podobnie jak miało to miejsce z kursorem statycznym). Zmienne kursora są najczęściej wykorzystywane wewnątrz procedur składowanych, które zwracają zmienne do programu działającego po stronie klienta. Zmienna kursora jest typem odwołania (jak wskaźniki w C). Może określać różne miejsca pamięci w czasie wykonywania programu. Deklaracja typu odwołania: REF typ, gdzie typ jest uprzednio zdefiniowanym typem. Słowo kluczowe REF wskazuje, że nowy typ będzie wskaźnikiem do zdefiniowanego typu. TYPE nazwa_typu IS REF CURSOR RETURN typ_rekordu; gdzie nazwa_typu jest nazwą nowego typu odwołania, a typ_rekordu jest typem rekordu wskazującego typy z listy wyboru, które będą ewentualnie zwrócone przez zmienną kursora. Zwracany typ zmiennej kursora musi być typem rekordu zadeklarowanym jawnie jako rekord zdefiniowany przez użytkownika lub niejawnie %ROWTYPE. Przykład 12 DECLARE TYPE t_pracownik IS REF CURSOR RETURN pracownik%ROWTYPE; TYPE t_rekord IS RECORD ( numer pracownik.numer%TYPE, nazwisko pracownik.nazwisko%TYPE ); zm_t_rekord t_rekord; TYPE t_numer_nazwisko IS REF CURSOR RETURN t_rekord; TYPE t_numer_nazwisko_2 IS REF CURSOR RETURN zm_t_rekord%TYPE; zm_t_pracownik t_pracownik; zm_t_numer_nazwisko t_numer_nazwisko; Przykład 12 zawiera ograniczone zmienne kursora – deklarowane tylko dla określonego typu zwracanych informacji. W deklaracji nieograniczonych zmiennych kursora nie ma klauzuli RETURN. Stąd mogą być otwarte dla dowolnego zapytania. Przykład 13 DECLARE TYPE t_pracownik IS REF CURSOR; zm_t_pracownik t_pracownik; Otwieranie zmiennej kursora dla zapytania OPEN zmienna_kursora FOR instrukcja_wyboru; UWAGA: Jeżeli zmienna kursora jest ograniczona, to lista wyboru musi odpowiadać typowi wyniku kursora. W zapytaniu sprawdzane są zmienne dowiązane i określany jest zbiór wynikowy. Z otwartej zmiennej kursora można pobierać dane instrukcją FETCH, a następnie zmienną kursową można zamknąć. Zamykanie zmiennej kursora CLOSE zmienna_kursora; Instrukcja CLOSE zwalnia zasoby użyte dla zapytania, ale niekoniecznie zwalnia obszar pamięci dla samej zmiennej kursora. Obszar pamięci zmiennej zwalniany jest wtedy, kiedy zmienna wychodzi poza zakres. Zamykanie kursora lub zmiennej kursora, które są już zamknięte, jest nieprawidłowe. Przykład 14 DECLARE TYPE t_pracownik IS REF CURSOR; zm_t_pracownik t_pracownik; zm_numer pracownik.numer%TYPE; zm_nazwisko pracownik.nazwisko%TYPE; BEGIN OPEN zm_t_pracownik FOR SELECT numer, nazwisko FROM pracownik; LOOP FETCH zm_t_pracownik INTO zm_numer, zm_nazwisko; EXIT WHEN zm_t_pracownik%NOTFOUND; DBMS_OUTPUT.PUT_LINE(zm_t_pracownik%ROWCOUNT || ' ' || zm_numer || ' ' || zm_nazwisko); END LOOP; CLOSE zm_t_pracownik; END; / UWAGI: • Zmienne kursora nie mogą być deklarowane w pakiecie; można w nim deklarować tylko typy. • Zdalne procedury nie mogą zwracać wartości zmiennej kursora. Zmienne kursora mogą być przekazywane pomiędzy mechanizmem PL/SQL po stronie klienta oraz po stronie serwera, ale nie pomiędzy dwoma serwerami. • Kolekcje języka PL/SQL (tabele indeksowe, tabele zagnieżdżone oraz tablice VARRAY) nie mogą zawierać zmiennej kursora. Podobnie tabele bazy danych i perspektywy nie mogą zawierać kolumn typu REF CURSOR. Można natomiast zdefiniować tablicę zmiennych kursora po stronie klienta. • Nie można używać zmiennych kursora z dynamicznym SQL w programie Pro*C. • Zapytanie skojarzone ze zmienną kursora w instrukcji OPEN … FOR … nie może wykorzystywać klauzuli FOR UPDATE. Ograniczenie zniesione od wersji Oracle8i. Wyjątek (ang. exception) • jest szczególnym zdarzeniem, które może zajść w trakcie wykonywania programu, np. dzielenie przez zero; • może być obsłużony przez związaną z nim procedurę nazywaną procedurą obsługi wyjątku (ang. exception handling); • w każdym programie mogą zostać wywołane dwa rodzaje wyjątków: • predefiniowane, wywoływane niejawnie przez system w trakcie działania programu lub wywoływane jawnie przez programistę; • definiowane przez użytkownika, wywoływane jawnie w ciele programu odpowiednim poleceniem. Aby wywołać wyjątek definiowany należy go najpierw zadeklarować w sekcji deklaracji bloku: DECLARE brak_danych EXCEPTION; Z każdym wyjątkiem (predefiniowanym i definiowanym) musi być związana procedura obsługi wyjątku, którą definiujemy w ostatnim bloku lub podbloku programu, oddzielonym od pozostałych bloków słowem kluczowym EXCEPTION: EXCEPTION /* początek bloku definiowania wyjątków */ WHEN brak_danych THEN /*jeśli wywołany wyjątek brak_danych */ INSERT INTO pracownicy VALUES (...); UWAGA: Jeśli chcemy aby ta sama procedura obsługi wyjątku ma być wykonywana dla różnych wyjątków, w klauzuli WHEN należy nazwy wyjątków oddzielić od siebie słowem kluczowym OR. Procedura obsługi wyjątków predefiniowanych jest wywoływana niejawnie w trakcie działania programu, może być również wywołana jawnie w ciele programu. Przykładowo, próba zamknięcia kursora, który nie został wcześniej otwarty spowoduje wywołanie wyjątku niejawnego, którego procedurę obsługi umieszczono po słowie kluczowym exception. Przykład 15 declare cursor pracownik_kursor is select numer, nazwisko, placa_pod from pracownik; pracownik_rekord pracownik_kursor%ROWTYPE; begin close pracownik_kursor; /* zamknięcie kursora, który nie został wcześniej otwarty */ exception when INVALID_CURSOR then null; end; / Wyjątek i związaną z nim procedurę obsługi wywołujemy jawnie poleceniem raise. select count(*) into liczba_prac from pracownicy; if liczba_prac = 0 then raise brak_danych; Wybrane predefiniowane wyjątki: DUP_VAL_ON_INDEX - wywoływany, gdy na skutek wykonania polecenia insert lub update różne rekordy relacji posiadają tę samą wartość atrybutu zadeklarowanego jako unikalny; INVALID_CURSOR - wywoływany w wyniku wykonania operacji na kursorze, która jest niepoprawna w pewnym kontekście, np. Zamknięcie nieotwartego kursora; INVALID_NUMBER - wywoływany, gdy nie jest możliwa konwersja łańcucha znaków do postaci liczby, ponieważ łańcuch zawiera niedopuszczalne wartości; LOGIN_DENIED - wywoływany w przypadku podania niepoprawnych parametrów w poleceniu rozpoczęcia sesji - login, np. nazwy użytkownika lub hasła; NO_DATA_FOUND - wywoływany, gdy polecenie select nie wyznacza żadnej krotki; NOT_LOGGED_ON - wywoływany w wyniku próby wykonania funkcji Oracle przez użytkownika nie dołączonego do systemu; PROGRAM_ERROR - wywoływany w wyniku błędnego działania programu PL/SQL; STORAGE_ERROR - wywoływany w wyniku błędu pamięci lub braku miejsca w pamięci; TIMEOUT_ON_RESOURCE - wywoływany po upłynięciu czasu oczekiwania na przydzielenie zasobu; TOO_MANY_ROWS - wywoływany, gdy jest wymagana dokładnie jedna krotka, a polecenie select wyznacza większą liczbę krotek; VALUE_ERROR - wywoływany w przypadku błędu konwersji typów danych (np. próby wpisania daty do numerycznego pola krotki) lub, gdy aktualna długość danej przekracza wcześniej zadeklarowaną długość (np. próba wpisania do pola krotki o długości 3 znaków danej o długości 5 znaków); ZERO_DIVIDE - wywoływany w wyniku próby dzielenia przez 0; OTHERS - wywoływany w dla wszystkich wyjątków, które nie zostały wyspecyfikowane w bloku exception; definiowany jako ostatni w bloku exception. Do uzyskania informacji o błędzie można uzyskać dzięki funkcjom: • SQLCODE – zwraca bieżący kod błędu, dla wyjątków zdefiniowanych przez użytkownika zwraca 1; • SQLERRM – zwraca tekst komunikatu o bieżącym błędzie, dla wyjątków zdefiniowanych przez użytkownika zwraca komunikat „Userdefined Exception”. Może być wywoływana z pojedynczym argumentem liczbowym: ▪ 0 – normalne, udane zakończenie; ▪ > 0 i <> +100 – wyjątek nie jest wyjątkiem systemowym Oracle; ▪ < 0 zwraca komunikat skojarzony z błędem; ▪ bez argumentu – zwraca zawsze normalne, udane zakończenie. Wyżej wymienione funkcje przypisywane są zmiennym lokalnym, które następnie stosowane są w instrukcjach SQL. Zad.6 Zdefiniuj kursor zawierający nazwiska i daty zatrudnienia wszystkich asystentów. Posłuż się tym kursorem do wyświetlenia rekordów w formie zdań „Asystent <nazwisko> pracuje od <data_zatrudnienia>”. Posłuż się poleceniem FETCH. Kolekcje Tablice indeksowane: TYPE nazwa_typu IS TABLE OF typ INDEX BY BINARY_INTEGER; Tablice indeksowane dla typów nieskalarnych: • rekordy TYPE nazwa_typu IS TABLE OF typ_rekord INDEX BY BINARY_INTEGER; Przykład 16 DECLARE TYPE t_etat IS TABLE OF etat%ROWTYPE INDEX BY BINARY_INTEGER; zm_t_etat t_etat; BEGIN SELECT * INTO zm_t_etat(1000) FROM etat WHERE nazwa = 'profesor'; DBMS_OUTPUT.PUT_LINE(zm_t_etat(1000).nazwa); zm_t_etat(1001) := 'audytor'; DBMS_OUTPUT.PUT_LINE(zm_t_etat(1001).nazwa); END; / • obiekty Zaczynamy od utworzenia typu obiektowego, a następnie można tworzyć egzemplarze gotowego typu obiektowego. Przykład 17 CREATE OR REPLACE TYPE Moj_obiekt AS OBJECT( pole_1 number, pole_2 varchar2(20), pole_3 date); / DECLARE TYPE t_obiektow IS TABLE OF Moj_obiekt INDEX BY BINARY_INTEGER; zm_obiektow t_obiektow; BEGIN zm_obiektow(1) := Moj_obiekt(1, NULL, NULL); zm_obiektow(1).pole_1 := 'Norbert'; zm_obiektow(1).pole_2 := sysdate; DBMS_OUTPUT.PUT_LINE(zm_obiektow(1).pole_1||' '|| zm_obiektow(1).pole_2||' '||zm_obiektow(1).pole_3);| END; / • tabele zagnieżdżone ◦ Tabele zagnieżdżone można tworzyć używając kluczy sekwencyjnych, a te nie mogą być liczbami ujemnymi. ◦ Tabele zagnieżdżone można zapisywać w bazie danych. ◦ Maksymalna liczba wierszy w tabeli zagnieżdżonej wynosi 2 GB (to maksymalna wartość klucza). Składnia: TYPE nazwa IS TABLE OF typ_tablicowy [NOT NULL]; gdzie typ_tablicowy może być typem wbudowanym, typem zdefiniowanym przez użytkownika lub wyrażeniem zawierającym %TYPE, ale nie może być to jeden z typów: BOOLEAN, NCHAR, NVARCHAR2 lub REF CURSOR. Gdy występuje fraza NOT NULL, elementy tabeli zagnieżdżonej nie mogą przyjmować wartości NULL. Przykład 18 DECLARE TYPE tabObiektow IS TABLE OF Moj_obiekt; zm_obiektow tabObiektow := tabObiektow(Moj_obiekt(1,'ala',sysdate)); BEGIN DBMS_OUTPUT.PUT_LINE(zm_obiektow(1).pole_1); END; / Metody kolekcji (tabele zagnieżdżone) Metoda Typ zwracanej wartości Opis Poprawne dla EXISTS BOOLEAN Sprawdza istnienie danego Tabele indeksowane, elementu kolekcji. tabele zagnieżdżone. COUNT NUMBER Zwraca liczbę elementów Tabele indeksowane, w kolekcji. tabele zagnieżdżone. LIMIT NUMBER Zwraca maksymalną liczbę elementów w kolekcji. FIRST (LAST) BINARY_INTEGER Zwraca pierwszy (ostatni) Tabele indeksowane, element kolekcji. tabele zagnieżdżone. Tabele zagnieżdżone (zawsze zwraca NULL). NEXT BINARY_INTEGER Zwraca następny (PRIOR) (poprzedni) element kolekcji względem danego. Tabele indeksowane, tabele zagnieżdżone. EXTEND N/A Dodaje element do kolekcji. Tabele zagnieżdżone. TRIM Usuwa element z końca kolekcji. Tabele zagnieżdżone N/A DELETE N/A Usuwa wybrane elementy Tabele indeksowane, kolekcji. tabele zagnieżdżone.