Wykład 3 Prowadzący: dr Paweł Drozda Zsbd PL/SQL Wykład 3 Prowadzący: dr Paweł Drozda
Program wykładu Wprowadzenie do PL\SQL Zmienne Typy danych Bloki, podbloki Funkcje SQL w PL/SQL Instrukcje warunkowe, pętle Złożone typy danych Rekordy Tabele Kursory dr P. Drozda
Co to jest PL/SQL i po co? Rozszerzenie SQL-a do języka proceduralnego Standardowy język Oracle dostępu do danych w relacyjnych bazach Ułatwia wykonywanie powtarzających się operacji jak również operacji opartych o określone warunki np. jeśli chcemy dać podwyżkę zależną od działu w którym pracownik jest zatrudniony dr P. Drozda
Podstawowe elementy PL/SQL Kod zorganizowany w wykonywalne bloki Zawiera elementy języka proceduralnego: Zmienne, stałe typy danych Instrukcje warunkowe, pętle Bloki danych używanie wiele razy – procedury, funkcje, pakiety Obsługa wyjątków dr P. Drozda
Zalety PL/SQL Integracja elementów proceduralnych z SQL-em Zwiększona wydajność Możliwość podziału kodu na moduły Integracja z narzędziami Oracle Obsługa wyjątków dr P. Drozda
Struktura bloku DECLARE (opcja)– deklaracja zmiennych BEGIN (konieczne) – zawiera instrukcje SQL i PL/SQL EXCEPTION (opcja) – działania gdy pojawią się błędy END; (konieczne) – kończy blok DECLARE … BEGIN … EXCEPTION … END; dr P. Drozda
Typy bloków Anonimowy Procedura Funkcja [DECLARE] BEGIN Instrukcje SQL i PL/SQL [EXCEPTION] END; PROCEDURE name IS BEGIN Instrukcje SQL i PL/SQL [EXCEPTION] END; FUNCTION name RETURN datatype IS BEGIN Instrukcje SQL i PL/SQL [EXCEPTION] END; dr P. Drozda
Przykład DECLARE name VARCHAR(30); BEGIN SELECT last_name INTO name FROM employees WHERE employee_id = 111; DBMS_OUTPUT.PUT_LINE(‘Nazwisko dla numeru 111 ’ || name); END; dr P. Drozda
Zmienne w PL/SQL Zaczynają się od litery Mogą zawierać cyfry oraz $,_ i # Maksymalnie 30 znaków Niedozwolone słowa kluczowe Przykład deklaracji: DECLARE data DATE; numer NUMBER NOT NULL :=10; miasto VARCHAR2(20):=‘Olsztyn’; ilosc CONSTANT NUMBER:=100; dr P. Drozda
Deklaracja zmiennych z %TYPE Jeśli zmienna odnosi się do zmiennej wcześniej zadeklarowanej lub do kolumny z tabeli Zmienna z atrybutem %TYPE przyjmuje typ danych odnośnika Ułatwienie uniknięcia błędnego typu danych Gdy zmienia się typ kolumny – zmiana typu zmiennej Przykład DECLARE name employees.last_name%TYPE; sal NUMBER(5,2); addition sal%TYPE :=100; dr P. Drozda
Typy danych – obiekty CLOB – duże ilości tekstu BLOB – duże obiekty binarne. Dane nie są interpretowane w bazie danych BFILE – pliki zawierające dane binarne. Nie są składowane w bazie – w bazie tylko wskazane miejsce, gdzie dany plik się znajduje NCLOB – zawiera obiekty opisujące znaki w różnych językach dr P. Drozda
Konwersja typów danych (1) Ukryta – gdy operacja łączy ze sobą różne typy danych Przykład DECLARE Numer number(5) :=200; Inny VARCHAR2(10) :=‘200’; Razem Numer%TYPE; BEGIN Razem := Numer + Inny; End; Powyższa konwersja stosowana pomiędzy: Znakami i datami Numerami i znakami dr P. Drozda
Konwersja typów danych (2) Jawna – stosowane wbudowane funkcje TO_CHAR – przekształca do znaków TO_DATE – przekształca do daty TO_NUMBER – przekształca do liczby TO_TIMESTAMP – zmienia ciąg znaków do typu timestamp dr P. Drozda
Podbloki – widoczność zmiennych Gdy zmienne mają tę samą nazwę – widoczna zmienna z bloku wewnętrznego Dostęp do zmiennej zewnętrznej – nadanie etykiety blokowi zewnętrznemu Przykład: begin <<zew>> DECLARE name varchar(20):=‘Tomasz'; BEGIN DECLARE name varchar(20):=‘Michal'; dbms_output.PUT_LINE(zew.name); END; end zew; dr P. Drozda
Przykład Pytania: Wartość info z (*) Wartość wszystko z (**) begin <<zew>> DECLARE zarobki NUMBER(7,2):=4000; dodatek NUMBER(7,2) := zarobki*0.2; info VARCHAR2(40):=‘dostaje’; BEGIN zarobki NUMBER(7,2):=40000; dodatek NUMBER(7,2) := 0; wszystko NUMBER(7,2):=zarobki+dodatek; (*)info := ‘Dyrektor nie’|| info; zew.dodatek:=zarobki*0.3; END; (**) info:= ‘Administrator ’|| info; end zew; Pytania: Wartość info z (*) Wartość wszystko z (**) Dodatek z (*) Zew.dodatek z (*) Dodatek z (**) Info z (**) dr P. Drozda
Operatory Logiczne Arytmetyczne Łączenia Problem NULL AND, OR, NOT +,-,*,/ Łączenia || Problem NULL Porównanie zawierające NULL zwraca zawsze NULL Użycie NOT do NULL zwraca NULL Gdy w warunku występuje NULL – przypisane operacje nie są wykonywane dr P. Drozda
Funkcje w PL/SQL Brak możliwości użycia w poleceniach PL/SQL (DECODE, AVG, SUM, MIN, MAX, etc) salary = AVG(employees.salary); - niedozwolone Mogą być użyte tylko w instrukcjach SQL osadzonych w PL/SQL SELECT AVG(salary) INTO srednia FROM employees WHERE department_id = 50; Funkcje odnoszące się do jednego wiersza mogą być używane w PL\SQL (LENGTH, CURRENT_DATE, MONTHS_BETWEEN) dr P. Drozda
Konwencja kodowania SQL, PL/SQL, Typy zmiennych – wielkie litery Zmienne, stałe, nazwy kolumn, nazwy tabel – małe litery Stosowanie wcięć dla czytelności kodu Przykład: DECLARE dept_no NUMBER(4); location_id NUMBER(4); BEGIN SELECT department_id, location_id INTO dept_no , location_id FROM departments WHERE department_name = ‘Sales’; END; dr P. Drozda
Nazewnictwo zmiennych Co się stanie? DECLARE department_name VARCHAR2(20):=‘SALES’; BEGIN DELETE FROM departments WHERE department_name = department_name; END; Należy unikać nazywania zmiennych tak samo jak nazwy kolumn – problemy w klauzuli WHERE Po SELECT oraz INTO – nie ma problemu dr P. Drozda
Polecenie SELECT w PL/SQL Składnia SELECT kolumny INTO zmienne FROM tabela [WHERE warunki]; Cechy: Słowo INTO jest obowiązkowe Liczba i typy kolumn zgadzają się z liczbą i typami zmiennych Otrzymane wyniki zapytania zapisywane są w zmiennych Zapytanie musi zwrócić dokładnie jeden wiersz dr P. Drozda
Przykład – polecenie SELECT DECLARE imie employees.first_name%TYPE; nazwisko employees.last_name%TYPE; BEGIN SELECT first_name, last_name INTO imie, nazwisko FROM employees WHERE employee_id = 111; END; dr P. Drozda
DML w PL/SQL Polecenia DML odpowiadają dokładnie poleceniom SQL (INSERT, UPDATE, DELETE, MERGE) Przykład MERGE INTO zatrudnienie z USING archiwum a ON (z.id_prac = a.ident) WHEN MATCHED THEN SET z.imie = a.imie; z.nazwisko = a.nazwisko; WHEN NOT MATCHED THEN INSERT VALUES (a.ident,a.imie, a.nazwisko); dr P. Drozda
Instrukcje warunkowe – IF Składnia IF (podobnie jak wszędzie) IF warunek THEN instrukcja; [ELSEIF warunek THEN instrukcja;] [ELSE instrukcja;] END IF; Przykład: DECLARE sal NUMBER(4); premia NUMBER(4); BEGIN IF sal > 5000 THEN premia = sal*0.1; ELSEIF sal > 3000 THEN premia= sal*0.2; ELSE premia = sal*0.3; END; dr P. Drozda
Instrukcje warunkowe CASE Składnia CASE pole WHEN warunek1 THEN instrukcje1 [WHEN warunek2 THEN instrukcje2] … [ELSE instrukcje] END; Przykład ocena_slownie = CASE ocena WHEN 5 THEN ‘bardzo dobry’ WHEN 4 THEN ‘dobry’ WHEN 3 THEN ‘dostateczny’ WHEN 2 THEN ‘niedostateczny’ WHEN 1,0 THEN ‘osioł’ ELSE ‘nie ma takiej oceny’ dr P. Drozda
Inny przykład CASE CASE WHEN ocena = 5 THEN ‘ bardzo dobry’ … WHEN ocena IN (1,0) THEN ‘osioł’ ELSE ‘nie ma takiej oceny’ END; dr P. Drozda
Pętla LOOP Wykonuje się do póki warunek nie jest spełniony Składnia pętli LOOP instrukcje; … EXIT WHEN warunek; END LOOP; Przykład DECLARE suma NUMBER(4):=0; i NUMBER(3):=1; suma:=suma+i; EXIT WHEN suma >100; i:=i+1; dr P. Drozda
Pętla WHILE Składnia WHILE Przykład: WHILE warunek LOOP DECLARE instrukcje; END LOOP; Przykład: DECLARE x NUMBER(4):=20; BEGIN WHILE x>0 LOOP x:=x-1; DBMS_OUTPUT.PUT_LINE(x); END; dr P. Drozda
Pętla FOR Składnia FOR Licznik nie musi być deklarowany FOR licznik IN dolnagr..gornagr [REVERSE] LOOP Instrukcje; END LOOP; Licznik nie musi być deklarowany Jeśli REVERSE licznik przechodzi od górnej granicy do dolnej Granice FOR nie powinny być NULL dr P. Drozda
FOR przykład DECLARE zarobki NUMBER(7):=2000; BEGIN FOR i IN 1..10 LOOP zarobki :=zarobki + i*1000; INSERT INTO EMP VALUES (ident.nextval, ‘kozak’,zarobki); END LOOP; END; dr P. Drozda
Kontrola wykonywania pętli CONTINUE wymusza porzucenie obecnej iteracji w pętli i rozpoczęcie nowej Może zwiększyć wydajność bloku EXIT wymusza opuszczenie obecnej pętli i przejście do kolejnej instrukcji Konieczne w podstawowej pętli LOOP dr P. Drozda
CONTINUE, EXIT - przykład DECLARE i NUMBER(4):=0; suma NUMBER(10):=0; BEGIN WHILE TRUE LOOP i:=i+1; IF MOD(i,2)=0 THEN CONTINUE; END IF; suma:=suma + i; IF i=10 THEN EXIT; END LOOP; END; dr P. Drozda
Złożone typy danych Rekordy %ROWTYPE Tabele INDEX BY Tabele nested tables Tabele VARRAY dr P. Drozda
Rekordy Zawierają dane logicznie ze sobą powiązane np. wiersz bądź część wiersza tabeli Mogą zawierać pola o różnych typach danych Zapisują tylko jeden wiersz (np. z tabeli) Muszą mieć co najmniej jedno pole Składnia rekordu: TYPE nazwa IS RECORD( definicjapole1, definicjapole2,…); dr P. Drozda
Przykład definicji rekordu TYPE pracownik IS RECORD( imie VARCHAR(20), id NUMBER(5), nazwisko VARCHAR(20), zarobki employees.salary%TYPE, miejsce departments%ROWTYPE); wystapienie pracownik; odwołanie do pola np. wystapienie.id – odwołanie do identyfikatora dr P. Drozda
%ROWTYPE Odwołuje się do wszystkich kolumn jednej tabeli Przed %ROWTYPE należy dodać nazwę tabeli do której ma odwoływać się zmienna Nazwy kolumn oraz typy danych w poszczególnych kolumnach przekazane do zmiennej Przy zmianie typów danych i nazw w tabeli – zmiana też w zmiennej z %ROWTYPE – nie trzeba pilnować typów Deklaracja: nazwapola nazwatabeli%ROWTYPE; osoba employees%ROWTYPE; odwołanie do pola (np. zarobki) osoba.salary dr P. Drozda
Przykład wykorzystania %ROWTYPE Tabela emp(id,name, salary,fire_date) DECLARE zmiana emp%ROWTYPE; BEGIN SELECT * INTO zmiana FROM emp WHERE id=111; zmiana.fire_date = CURRENT_DATE; UPDATE emp SET ROW zmiana WHERE id=111; END; dr P. Drozda
Tabele INDEX BY Składają się z dwóch pól – identyfikatora (liczba lub ciąg znaków) oraz wartości Gromadzą dane o tym samym typie – np. imiona z tabeli employees Mają nieograniczoną wielkość tzn. ogranicza je zakres wartości identyfikatora Odpowiadają tabelom z „normalnych” języków programowania dr P. Drozda
Tworzenie tabel – INDEX BY TYPE nazwa IS TABLE OF typkolumny INDEX BY PLS_INTEGER,|BINARY_INTEGER|VARCHAR2; Przykład DECLARE TYPE tabelaimiona IS TABLE OF employees.first_name%TYPE INDEX BY PLS_INTEGER; imiona tabelaimiona; BEGIN imiona(1):=‘Zenek’; END; dr P. Drozda
Funkcje odwołujące się do tabel INDEX BY EXISTS – sprawdza czy istnieje dana komórka w tabeli np. IF imiona(1).EXISTS THEN INSERT INTO …; COUNT – zwraca liczbę elementów z tabeli dbms_output.put_line(imiona.COUNT); FIRST, LAST – zwracają pierwszą/ostatnią wartość z tabeli; gdy tabela pusta – zwranany jest NULL PRIOR(n), NEXT(n) – zwracają poprzedni/kolejny indeks z tabeli – porównując do n DELETE – usuwa elementy z tabeli bez argumentu – usuwa wszystko z argumentem n – usuwa n-ty element z argumentami m,n – usuwa wszystkie elementy od m do n dr P. Drozda
Przykład INDEX BY DECLARE TYPE typpracownik IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER; pracownik typpracownik; BEGIN FOR i IN 100..105 LOOP SELECT * INTO pracownik(i) FROM employees WHERE employee_id=i; END LOOP; FOR i IN pracownik.FIRST..pracownik.LAST DBMS_OUTPUT.PUT_LINE(pracownik(i).last_name, pracownik(i).first_name); END; dr P. Drozda
Tabele nested tables Składnia TYPE nazwa IS TABLE OF typ_danych; Nie mają z góry określonej wielkości Maksymalny rozmiar 2GB Odwołania do poszczególnych wartości – tak jak w tabelach INDEX BY (w kluczach nie może być ujemnych wartości) Wiersze nie są poustawiane w jakimś porządku – przeszukiwanie rekord po rekordzie (INDEX BY – mają klucz indeksowany) dr P. Drozda
Przykład – nested tables DECLARE TYPE typ_lokacji IS TABLE OF locations.city%TYPE; miasta typ_lokacji; BEGIN miasta:=typ_lokacji(‘Gniewkowo’, ’Solec’, ’Pszczólki’, ’Książki’); FOR i IN 1..miasta.count() LOOP dbms_output(miasta(i)); END LOOP; END; dr P. Drozda
Tabele VARRAY Ustalona w deklaracji wielkość tabeli Składnia tworzenia tabeli VARRAY TYPE nazwa IS VARRAY(n) OF typDanych; Maksymalny rozmiar 2GB dr P. Drozda
Kursory Definiowane do poleceń SELECT, które zwracają wiele wierszy do przechowywania danych Można przetwarzać wiersz po wierszu Zasada działania: EMPTY? YES DECLARE OPEN FETCH CLOSE NO dr P. Drozda
Składnia deklaracji kursora CURSOR nazwa IS zapytanie SQL (SELECT); Przykład: DECLARE CURSOR dept_30 IS SELECT last_name, first_name, salary FROM employees WHERE department_id = 30; dr P. Drozda
Otwieranie kursora – OPEN Aby była możliwość dostępu do danych w kursorze należy użyć polecenia – OPEN nazwa; Polecenie alokuje dynamicznie pamięć dla danych z otwieranego kursora Odczytywanie są odpowiednie typy i nazwy kolumn dla kursora Wiersze nie są przekazywane do zmiennych kursora – dopiero w momencie uruchomienia polecenia FETCH dane są czytane dr P. Drozda
Przykład DECLARE CURSOR dane IS SELECT first_name, last_name FROM employees WHERE department = 50; imie employees.first_name%TYPE; nazwisko employees.last_name%TYPE; BEGIN OPEN dane; FETCH dane INTO imie, nazwisko; DBMS_OUTPUT.PUT_LINE(imie || ‘ ’|| nazwisko); END; dr P. Drozda
FETCH, CLOSE FETCH CLOSE pobiera z kursora jeden wiersz danych i przesuwa się do następnego wiersza Po INTO liczba zmiennych i typy muszą odpowiadać liczbie i typom kolumn pobieranych z kursora Kolejność występowania zmiennych musi odpowiadać kolejności kolumnom zdefiniowanych w kursorze FETCH nazwa INTO zmienne; CLOSE Zamyka dostęp do kursora Zwalnia pamięć zajmowaną przez kursor Dobra praktyka zawsze zamykać, gdy niepotrzebny CLOSE nazwa; dr P. Drozda
Użycie rekordów w kursorach Przy deklaracji zmiennej – typ ustawiany jako nazwakursora%ROWTYPE; Przy poleceniu FETCH wiersz wpisywany do zadeklarowanego rekordu Przykład: DECLARE CURSOR dane …; wiersz dane%ROWTYPE; BEGIN OPEN dane; FETCH dane INTO wiersz; … END; dr P. Drozda
Przykład – pełna wersja DECLARE CURSOR dane IS SELECT first_name, last_name FROM employees WHERE department_id = 50; wiersz dane%ROWTYPE; BEGIN OPEN dane; LOOP FETCH dane INTO wiersz; EXIT WHEN dane%NOTFOUND; DBMS_OUTPUT.PUT_LINE(wiersz.first_name, ‘ ‘ , wiersz.last_name); END LOOP; dane%ROWCOUNT; CLOSE dane; END; dr P. Drozda
FOR dla kursora Składnia FOR zmienna IN nazwakursora LOOP instrukcje; END LOOP; Otwarcie, FETCH i zamknięcie kursora przez pętle Zmienna – rekord sczytujący kolejne wiersze kursora dr P. Drozda
Przykład DECLARE CURSOR dane IS SELECT first_name, last_name FROM employees WHERE department_id = 50; BEGIN FOR wiersz IN dane LOOP DBMS_OUTPUT.PUT_LINE(wiersz.first_name|| ‘ ‘ || wiersz.last_name); END LOOP; DBMS_OUTPUT.PUT_LINE(dane%ROWCOUNT); END; dr P. Drozda
Atrybuty kursora %ISOPEN – zwraca prawdę gdy kursor otwarty %NOTFOUND – zwraca prawdę gdy zmienna nie zawiera danych %FOUND – zwraca prawdę gdy zmienna zawiera dane %ROWCOUNT – zwraca liczbę wierszy wyciągniętych poprzez kursor do momentu wywołania atrybutu dr P. Drozda
Przykład bez deklaracji kursora BEGIN FOR dane IN (SELECT last_name, first_name FROM employees) LOOP … END LOOP; END; dr P. Drozda
Kursor – parametry Do kursora mogą zostać dodane parametry Skłania: CURSOR nazwa(parametr typ, …) IS SELECT …; BEGIN OPEN nazwa(wartosc parametru); dr P. Drozda
Przykład DECLARE CURSOR ponumerze (depid NUMBER) IS SELECT last_name, first_name, salary FROM employees WHERE department_id=depid; BEGIN OPEN ponumerze(10); CLOSE ponumerze; OPEN ponumerze(50); … END; dr P. Drozda