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