Wydział Elektrotechniki, Informatyki i Telekomunikacji
Transkrypt
Wydział Elektrotechniki, Informatyki i Telekomunikacji
Wydział Elektrotechniki, Informatyki i Telekomunikacji Instytut Informatyki i Elektroniki Instrukcja do zajęć laboratoryjnych Nr ćwiczenia: 2 Temat: Przypomnienie wiadomości na temat języka PL/SQL UWAGI: Parametry wejściowe procedur powinny być zdefiniowane jako zakotwiczone (ang. anchor). Ta sama uwaga dotyczy się również wszystkich innych zmiennych odnoszących się do poszczególnych kolumn w tabelach. W wszystkich procedurach i funkcjach należy dokładnie przemyśleć oraz zaimplementować obsługę ew. błędów, tak aby programy nie „wysypywały” się w sposób niekontrolowany. Zwrócić również uwagę na konwencje w nazewnictwie zmiennych (powinna być jednolita we wszystkich programach). Proponujemy używać przedrostków in_, inout_ oraz out_ w nazwach parametrów wejściowych procedur i funkcji (w zależności od trybu zmiennej – patrz wykład) oraz przedrostków c_ w nazwach kursorów. Pozostałe zmienne mogą mieć przedrostek uv_ (od user variable) aby łatwiej je było odróżnić od np. identyfikatorów nazw kolumn. Proponujemy również przyjąć zasadę, że wszystkie słowa kluczowe języka SQL oraz PL/SQL piszemy dużymi literami a pozostałe identyfikatory definiowane przez użytkownika małymi. Bardzo wydatnie zwiększa to czytelność programów! Należy również pamiętać o konsekwentnym i jednolitym stosowaniu wcięć, których brak bardzo utrudnia czytanie programów. NAJPROSTSZE PROGRAMY W JĘZYKU PL/SQL. BLOKI ANONIMOWE. OBSŁUGA WYJĄTKÓW: 1. Napisz anonimowy blok PL/SQL, w którym zostaną zadeklarowane cztery zmienne. Jedna będzie typu NUMBER, druga typu VARCHAR2, trzecia typu DATE. Zmienne te powinny zostać zainicjowane dowolnymi wartościami. Czwarta zmienna, typu DATE, powinna być zadeklarowana jako CONSTANT i również zainicjowana dowolną wartością. W dalszej części programu należy wyświetlić na konsoli wartości tych zmiennych (użyć funkcji PUT_LINE z pakietu DBMS_OUTPUT). Spróbować również programowo zmodyfikować czwartą zmienną (o atrybucie CONSTANT) i zaobserwować reakcję systemu Oracle. 2. Napisz anonimowy blok PL/SQL, który wypisze ile dni minęło od twojej daty urodzenia. 3. Napisz anonimowy blok PL/SQL, który będzie pobierał od użytkownika 2 liczby (z konsoli SQL*Plus-a) a następnie wyświetli ich sumę, różnicę, iloczyn, iloraz oraz wartość pierwszej liczby podniesionej do potęgi wskazywanej przez drugą liczbę. Studiując samodzielnie opracował: dr inż. Artur Gramacki 1 dokumentację wykonać jeszcze trzy inne, dowolnie wybrane działania, na podanych liczbach. Pobieranie liczb z konsoli może wyglądać jak poniżej: ... Podaj wartość dla liczba1: 10 stare 2: licz1 NUMBER := &liczba1; nowe 2: licz1 NUMBER := 10; Podaj wartość dla liczba2: 40 stare 3: licz2 NUMBER := &liczba2; nowe 3: licz2 NUMBER := 40; Suma: 50 Procedura PL/SQL została zakończona pomyślnie. SQL> Następnie postaraj się tak ustawić odpowiednie zmienne środowiska SQL*Plus, aby wyłączyć wypisywanie na ekranie informacji o pobieranych zmiennych. ... Podaj wartość dla liczba1: 10 Podaj wartość dla liczba2: 20 Suma: 30 Procedura PL/SQL została zakończona pomyślnie. SQL> 4. Napisz anonimowy blok PL/SQL, który poprosi użytkownika o podanie liczby całkowitej N a następnie wpisze liczby od 1 do N. Napisać trzy wersje programu, każdy z wykorzystaniem innego rodzaju pętli ( LOOP, FOR-LOOP, WHILE-LOOP). Za pomocą zdefiniowanego wyjątku sprawdzić, czy podana liczba mieści się w przedziale <1...10>. Użyj procedury RAISE_APPLICATION_ERROR do zdefiniowania własnego błędu (tak, jest to możliwe!). 5. Napisz anonimowy blok PL/SQL, który wyświetli podstawowe dane o pracowniku (tabela EMP), który zarabia najmniej oraz najwięcej. Użyj tzw. kursora niejawnego. Definiując wymagane zmienne użyj tzw. deklaracji zakotwiczonych (ang. anchor). Pamiętaj, że należy obsłużyć sytuację, gdy istnieje więcej niż jeden pracownik zarabiający najmniej lub najwięcej. W takim przypadku wyświetl ilość rekordów spełniających te warunki, np.: Najwięcej zarabia: Imię1 Nazwisko1. Zarobki: 2000 Najmniej zarabia: Imię2 Nazwisko2: Zarobki: 550 lub Pracowników, którzy zarabiają najmniej jest: 3. Zarobki 2000 Pracowników, którzy zarabiają najwięcej jest: 2. Zarobki 550 6. Napisz anonimowy blok PL/SQL. Zdefiniuj tzw. kursor jawny, który pobierze i wyświetli dane wszystkich pracowników. Zastosuj pętlę LOOP ... END LOOP. 7. Powtórz polecenie z poprzedniego punktu ale tym razem użyj pętli FOR ... IN, aby uniknąć jawnego deklarowania kursora (CURSOR...IS...), pobierania rekordów (za pomocą FETCH) oraz zamykania kursora (CLOSE). opracował: dr inż. Artur Gramacki 2 8. Napisz anonimowy blok PL/SQL i zdefiniuj kursor z parametrami. Jego zadaniem ma być wyświetlenie wszystkich zamówień (tabela ORD) złożonych w podanym okresie (np. zmienne DATA_OD, DATA_DO). Wyświetlając dane o zamówieniu pobieraj również dane o kliencie, którego dotyczy zamówienie (z tabeli CUSTOMER) oraz dane o pracowniku obsługującym dane zamówienie (tabela EMP). PAKIETY, PROCEDURY, FUNKCJE PL/SQL: 1. ADD_EMP – zadaniem procedury ma być dodawanie nowego pracownika do tabeli EMP. Numer ID powinien być pobierany automatycznie ze zdefiniowanej w tym celu sekwencji. 2. CHANGE_EMP – procedura modyfikuje dane wskazanego pracownika. 3. DELETE_EMP – procedura kasuje dane wskazanego pracownika. 4. CHANGE_SALARY – zadaniem tej procedury jest zmiana zarobków wskazanego pracownika. Podajemy procentową zmianę zarobków. Przykładowo, gdy podamy 50 oznacza to wzrost zarobków o 50% a gdy podamy -10 oznacza to obniżkę zarobków o 10%. 5. TOP_N_EMP – wyświetla listę N pracowników (N podawane jako parametr wejściowy procedury), którzy zarabiają najwięcej. Dane o tych pracownikach (imię, nazwisko, zarobki) powinni zostać dodatkowo zapisani do tabeli o nazwie TOP_N_EMP (czy tabelę tą można stworzyć bezpośrednio w kodzie procedury ?). 6. CHANGE_DEPT – procedura zmienia przypisanie pracownika do wydziału (tabela DEPT). 7. CURRENCY_SALARY – zadaniem procedury jest wypisanie zarobków w podanej jako parametr walucie. Dane na temat kursów walut powinny być pobierane z utworzonej wcześniej tabeli CURRENCY (kolumny: id, currency_symbol, rate). Przykładowo wywołanie funkcji w postaci CURRENCY_SALARY('PLN') wypisze zarobki pracowników po przeliczeniu ich na złotówki. Zakładamy, że zarobki, wpisane w tabeli EMP są w walucie USD. 8. STAT_EMP – zadaniem funkcji jest zwrócenie wartości (w zależności od podanego parametru) zarobków maksymalnych, minimalnych, średniej lub też sumy zarobków wszystkich pracowników. Funkcja powinna przyjmować tylko jeden z czterech parametrów: MAX, MIN, AVG, SUM. Podanie innego parametru powinno wygenerować stosowne ostrzeżenie. 9. ORD_SUM – zadaniem funkcji jest zwrócenie całkowitej wartości wskazanego zamówienia (suma iloczynów ITEM.PRICE * ITEM.QUANTITY). Wewnątrz funkcji powinno wykonać się sprawdzenie spójności danych z danymi w tabeli ORD (patrz następny punkt). Gdy dane są niespójne zamiast sumy iloczynów powinien być zwrócony stosowny komunikat). 10. CHECK_ORD_ITEM – zadaniem procedury jest sprawdzenie, czy dane w tabelach ORD oraz ITEM są spójne. W tabeli ORD występuje kolumna TOTAL natomiast w tabeli ITEM występują kolumny PRICE oraz QUANTITY (patrz rysunek poniżej). Zgodnie z logiką modelu SUMMIT2 mamy, że kolumna ORD.TOTAL zawiera (powinna zawierać) sumaryczną wartość sprzedaży na danym zamówieniu. Jest ona ilościowo równa wartości wszystkich zamówionych produktów, których szczegóły zapisane są w tabeli ITEM (czyli jest to suma wszystkich iloczynów ITEM.PRICE * ITEM.QUANTITY dla danego zamówienia). opracował: dr inż. Artur Gramacki 3 fragment tabeli ITEM fragment tabel ORD ITEM_ID <pk> <fk> ID ORD_ID TOTAL PRICE QUANTITY Procedura ma wyświetlić dane zamówień, które są niespójne. Na ekranie powinniśmy zobaczyć wynik podobny jak w poniższym przykładzie (pokazano przypadek, gdy dwa rekordy w tabeli ORD nie są spójne w sensie podanym powyżej): Numer zamówienia: total : price * quantity: ord_id=1: 100 * ord_id=2: 20 * ord_id=3: 10 * 100 5000 10 = 1000 40 = 800 50 = 500 -----2300 Numer zamówienia: 101 total : 3000 price * quantity: ord_id=10: 10 * 10 = 100 ord_id=20: 20 * 50 = 1000 -----1100 WYZWALACZE BAZODANOWE: 1. Napisać wyzwalacz dla tabeli EMP, który sprawdza czy w polu SALARY nie wpisano błędnie liczby ujemnej (zarobki pracownika nie mogą być ujemne!). 2. Jak wiadomo w tabeli EMP możliwe jest przechowywanie informacji o odległościach pracowników (kolumny ID oraz MANAGER_ID). Napisać wyzwalacz, który będzie kontrolował, czy wpisywane dla danego pracownika zarobki nie są większe niż zarobki jego bezpośredniego szefa. Poniżej pokazano część danych z tabeli EMP. Wynika z niej, że pracownicy o numerach ID=20 oraz ID=21 nie mogą zarabiać więcej niż 1100, gdyż tyle zarabia ich bezpośredni przełożony. Podobnie pracownicy o numerach ID=22 oraz ID=23 nie mogą zarabiać więcej niż 1300. ID MANAGER_ID Imie, Nazwisko SALARY ---------- ---------- ------------------------------ ---------8 2 Ben Biri 1100 20 8 ..Chad Newman 750 21 8 ..Alexander Markarian 850 9 2 Antoinette Catchpole 1300 22 9 ..Eddie Chang 800 23 9 ..Radha Patel 795 3. Napisz wyzwalacz, który będzie kontrolował (i ew. poprawiał), czy wpisując do tabeli EMP imię oraz nazwisko pracownika pierwsza litera jest duża (przykładowo imię "Marek" powinno zostać opracował: dr inż. Artur Gramacki 4 przyjęte bez zmian a imię "marek" powinno zostać automatycznie skorygowane do postaci "Marek"). 4. Do tabeli DEPT dodać kolumnę NUMBER_OF_EMP (poleceniem ALTER TABLE ...). Zadaniem tej kolumny będzie przechowywanie informacji o aktualnej liczbie pracowników, którzy pracują w danym dziale. Napisać wyzwalacz, który będzie dbał o aktualność danych w tej kolumnie. Poniższe zapytanie wylicza liczbę pracowników w poszczególnych działach. Utworzony wyzwalacz powinien automatycznie wyliczać tą liczbę i uaktualniać kolumnę NUMBER_OF_EMP. Powinien on uruchamiać się w przypadku rejestrowania nowego pracownika, kasowania istniejącego pracownika lub też modyfikacji istniejącego. SELECT r.name "Region", d.name "Dział", count(*) "Liczba prac." FROM emp E, dept D, region R WHERE E.dept_id = D.id AND D.region_id = R.id GROUP BY D.name, D.region_id, R.name; Region -------------------North America North America North America South America Africa / Middle East Asia Europe North America South America Africa / Middle East Asia Europe Dział Liczba prac. ---------------- -----------Administration 2 Finance 1 Operations 4 Operations 3 Operations 3 Operations 2 Operations 3 Sales 2 Sales 1 Sales 1 Sales 2 Sales 1 12 wierszy zostało wybranych. 5. Napisać wyzwalacz, który będzie sprawdzał, czy dane w tabelach ORD oraz ITEM są spójne. Jeżeli stwierdzona zostanie niespójność wyzwalacz powinien odpowiednio zmodyfikować wartość kolumny TOTAL w tabeli ORD. Zgodnie z logiką modelu SUMMIT2 mamy, że kolumna ORD.TOTAL zawiera (powinna zawierać) sumaryczną wartość sprzedaży na danym zamówieniu. Jest ona ilościowo równa wartości wszystkich zamówionych produktów, których szczegóły zapisane są w tabeli ITEM (czyli jest to suma wszystkich iloczynów ITEM.PRICE * ITEM.QUANTITY dla danego zamówienia). fragment tabeli ITEM fragment tabel ORD ITEM_ID <pk> <fk> ID ORD_ID TOTAL PRICE QUANTITY opracował: dr inż. Artur Gramacki 5 6. Zaprojektować a następnie zaimplementować (korzystając z odpowiednio zbudowanego wyzwalacza) mechanizm pozwalający rejestrować wszelkie operacje (w praktyce chodzi o INSERT, UPDATE, DELETE) wykonywane na tabeli EMP. Chodzi tu o rodzaj historii zmian zachodzących w tabeli EMP. Poniżej pokazano przykład działania opisanego wyżej mechanizmu dla bardzo prostej tabeli TEST. Na początek utwórzmy tą tabelę i wpiszmy do niej przykładowe dane: DROP TABLE test; CREATE TABLE test (id NUMBER, kol VARCHAR2(10)); INSERT INTO test VALUES (100, 'a'); INSERT INTO test VALUES (101, 'b'); INSERT INTO test VALUES (102, 'c'); SELECT * FROM test; Tabela TEST wygląda więc następująco: ID KOL 100 a 101 b 102 c Tabela TEST_HIST rejestrująca zmiany, które dokonują się w tabeli TEST niech wygląda jak poniżej: ID KOL ID_HIST TYP DATA 100 a 1001 U 09-09-2003 100 aa 1002 U 12-10-2003 100 aaa 1003 U 15-10-2003 103 d 1004 I 12-11-2003 101 b 1005 U 25-11-2003 101 bb 1006 D 29-11-2003 100 aaaa 1007 D 01-12-2003 Zawiera ona wszystkie kolumny tabeli TEST plus kilka kolumn "systemowych", które zaznaczono szarym kolorem. Widać, że znajduje się w niej 7 rekordów. Rekordy te zostały wstawione automatycznie przez odpowiednio zaprojektowany wyzwalacz. Są one efektem wykonania na tabeli TEST czterech operacji UPDATE jednej operacji INSERT oraz dwóch operacji DELETE: UPDATE UPDATE UPDATE INSERT UPDATE DELETE DELETE test test test INTO test FROM FROM SET kol='aa' WHERE id='100'; SET kol='aaa' WHERE id='100'; SET kol='aaaa' WHERE id='100'; test VALUES (103, 'd'); SET kol='bb' WHERE id='101'; test WHERE id=101; test WHERE id=100; SELECT * FROM test; opracował: dr inż. Artur Gramacki 6 Zwróćmy uwagę na kolumnę ID_HIST, która pozwala nam odtworzyć kolejność dokonywanych w tabeli TEST zmian (liczby te najwygodniej generować za pomocą sekwencji). Kolumna TYP przechowuje symbol wykonywanej operacji (U – UPDATE, I – INSERT, D – DELETE). Tabela TEST po wykonaniu powyższych operacji ma następującą zawartość: ID KOL 102 c 103 d Zwróćmy uwagę, że dane tabelę TEST_HIST można bardzo łatwo wykorzystać do wykonania operacji odtwarzających pierwotny stan tabeli TEST (ang. undo). W poniższej tabeli pokazano operacje, które należy wykonać aby powrócić do stanu pierwotnego: TYP D Operacja do wykonania INSERT INTO test VALUES (100, 'aaaa'); D INSERT INTO test VALUES (101, 'bb'); U UPDATE test SET id=101, kol='b' WHERE id='101'; I DELETE FROM test WHERE id='103'; U UPDATE test SET id=100, kol='aaa' WHERE id='100'; U UPDATE test SET id=100, kol='aa' WHERE id='100'; U UPDATE test SET id=100, kol='a' WHERE id='100'; Z kolei dzięki kolumnie DATA można zrealizować operację automatycznego odtwarzani stanu tabeli TEST do określonego punktu w przeszłości (ang. point in time recovery). Czy potrafiłbyś stworzyć procedurę PL/SQL która będzie to realizowała? Zastanów się, czy równie prosto można zrealizować problem odtwarzania do określonego punktu w przeszłości stanu wielu powiązanych ze sobą tabel (więzami integralności FOREIGN KEY)? opracował: dr inż. Artur Gramacki 7