Ćwiczenia 1 powtórzenie z SQL Bazy Danych 2007/2008 by Dzik. 1. Wyswietlic nazwy i liczbe zatrudnionych z tych wydzialow o najwiekszej liczba aktualnie zatrudnionych osob WITH pomoc AS (SELECT w.nazwa, COUNT(z.id_wydz) liczba_os WHERE o.id = z.id_os AND w.id = z.id_wydz AND z.do IS NULL GROUP BY w.nazwa) SELECT * FROM pomoc WHERE liczba_os = (SELECT MAX(liczba_os) FROM pomoc); 2. Wyswietlic te osoby aktualnie zatrudnione, ktore aktualnie pobieraja maksymalna pensje SELECT o.*, p.pensja FROM osoby o, zatrudnienia z, pensje p WHERE o.id = z.id_os AND o.id = p.id_os AND z.do IS NULL AND p.do IS NULL AND p.pensja = (SELECT MAX(pp.pensja) FROM osoby oo, pensje pp, zatrudnienia zz WHERE oo.id = zz.id_os AND oo.id = pp.id_os AND zz.do IS NULL); 3. Wyswietlic na kazdym z wydzialow osoby aktualnie zatrudnione pobierajace aktualnie maksymalna pensje SELECT w.nazwa, o.*, p.pensja FROM osoby o, wydzialy w, pensje p, zatrudnienia z WHERE o.id = z.id_os AND o.id = p.id_os AND z.do IS NULL AND p.do IS NULL AND w.id = z.id_wydz AND p.pensja = (SELECT MAX(pp.pensja) FROM osoby oo, wydzialy ww, pensje pp, zatrudnienia zz WHERE oo.id = zz.id_os AND oo.id = pp.id_os AND zz.do IS NULL AND pp.do IS NULL AND ww.id = zz.id_wydz AND w.nazwa = ww.nazwa);
Ćwiczenia 2 bloki anonimowe i kursory niejawne (SELECT INTO) 1. Napisac kod bloku anonimowego w języku PL/SQL za pomoca ktorego z tabeli Osoby bedzie mozna wybrac osobe o okreslonym Nazwisku i Imieniu (Lis, Jan) ktore zostana zadeklarowane poprzez odpowiednie zainicjowanie w sekcji deklaracji programu dwoch zmiennych tekstowych o wartosciach Lis i Jan. Komunikat: Osoba wybrana jest osoba Lis Jan! z_imie osoby.imie1%type; z_nazwisko osoby.nazwisko%type; SELECT o.nazwisko, o.imie1 INTO z_nazwisko, z_imie FROM osoby o WHERE INITCAP(o.nazwisko) = 'Lis' AND INITCAP(o.imie1) = 'Jan'; -- INITCAP ustawia pierwsza litere stringa na wielką, a resztę na małe DBMS_OUTPUT.PUT_LINE('Wybrana osoba to ' z_nazwisko ' ' z_imie); 2. Jak w zadaniu 1, z tymze nalezy znalezc osobe poprzez zadeklarowanie jednej zmiennej PL/SQL-owej o wartosci Lis Jan. z_imienazwisko VARCHAR2(30); SELECT INITCAP(o.imie1) ' ' INITCAP(o.nazwisko) INTO z_imienazwisko FROM osoby o WHERE INITCAP(o.imie1) = 'Jan'AND INITCAP(o.nazwisko) = 'Lis'; DBMS_OUTPUT.PUT_LINE('Wybrana osoba jest ' z_imienazwisko);
3. Za pomoca anonimowego bloku PL/SQL-owego wyswietlic na danym wydziale ktorego nazwa jest zainicjowana zmienna PL/SQL-owa o wartosci Matematyka. Dane tej osoby, aktualnie na niej zatrudnionej, ktorej suma dlugosci nazwiska i imienia jest najwieksza. Komunikat: osoba aktualnie zatrudniona na wydziale Matematyka o najwiekszej sumie dlugosci imienia i nazwiska jest osoba o id = 8, tj. Duda Barbara, a dl wynosi 11. z_wydzial VARCHAR2(20) := 'Matematyka'; z_imienazwisko VARCHAR2(50); z_dlugosc NUMBER; z_id osoby.id%type; SELECT o.id, o.imie1 ' ' o.nazwisko, LENGTH(o.imie1 ' ' o.nazwisko) INTO z_id, z_imienazwisko, z_dlugosc FROM osoby o, zatrudnienia z, wydzialy w WHERE o.id = z.id_os AND z.do IS NULL AND w.id = z.id_wydz AND INITCAP(w.nazwa) = z_wydzial AND LENGTH(o.imie1 ' ' o.nazwisko) = (SELECT MAX(LENGTH(oo.imie1 ' ' oo.nazwisko)) FROM osoby oo, zatrudnienia zz, wydzialy ww WHERE oo.id = zz.id_os AND zz.do IS NULL AND ww.id = zz.id_wydz AND w.nazwa = ww.nazwa); DBMS_OUTPUT.PUT_LINE('Osoba aktualnie zatrudniona na wydziale ' z_wydzial ' o najwiekszej sumie dlugosci imienia i nazwiska jest osoba o id = ' z_id ', tj. ' z_imienazwisko ', a dlugosc wynosi ' z_dlugosc);
4. Za pomoca bloku anonimowego PL/SQL wyswietlic na danym wydziale ktorego nazwa jest zainicjowana zmiennia PLSQL najmlodsza aktualnie zatrudniona kobieta i najmlodszego aktualnie zatrudnionego mezczyzne. z_nazwa VARCHAR2(20) := 'Matematyka'; z_imienazwiskom VARCHAR2(50); z_id osoby.id%type; z_data DATE; SELECT o.id, o.imie1 ' ' o.nazwisko, o.d_ur INTO z_id, z_imienazwisko, z_data WHERE o.plec = 'K' AND o.id = z.id_os AND w.id = z.id_wydz AND z.do IS NULL AND INITCAP(w.nazwa) = z_nazwa AND o.d_ur = (SELECT MAX(oo.d_ur) FROM osoby oo, wydzialy ww, zatrudnienia zz WHERE oo.plec = 'K' AND oo.id = zz.id_os AND ww.id = zz.id_wydz AND zz.do IS NULL AND ww.nazwa = w.nazwa); DBMS_OUTPUT.PUT_LINE('Najmlodsza kobieta zatrudniona na wydziale ' z_nazwa ' jest osoba o id = ' z_id ', ' z_imienazwisko ' urodzona w dniu ' z_data); SELECT o.id, o.imie1 ' ' o.nazwisko, o.d_ur INTO z_id, z_imienazwisko, z_data WHERE o.plec = 'M' AND o.id = z.id_os AND w.id = z.id_wydz AND z.do IS NULL AND INITCAP(w.nazwa) = z_nazwa AND o.d_ur = (SELECT MAX(oo.d_ur) FROM osoby oo, wydzialy ww, zatrudnienia zz WHERE oo.plec = 'M' AND oo.id = zz.id_os AND ww.id = zz.id_wydz AND zz.do IS NULL AND ww.nazwa = w.nazwa); DBMS_OUTPUT.PUT_LINE('Najmlodszym mezczyzna zatrudnionym na wydziale ' z_nazwa ' jest osoba o id = ' z_id ', ' z_imienazwisko ' urodzona w dniu ' z_data);
Ćwiczenia 3 rekordy, ROWID 1. a) Zdefiniowac rekord t_rekordosoba przeznaczony do przechowywania wspolnych danych osoby z tabelki osoby, id, nazwisko, imie, data_ur, plec. Pobrac dane przykladowej osoby za pomoca zmiennej rekordowej tego typu i wyswietlic je na ekranie. b) za pomoca typu rekordowego zdefiniowanego deklaracja zakotwiczona -- rekord dla podpunktu a) TYPE t_rekordosoba1 IS RECORD (z_id NUMBER, z_nazwisko VARCHAR2(20), z_imie VARCHAR2(20), z_data DATE, z_plec CHAR(1) ); -- rekord dla podpunktu b) TYPE t_rekordosoba2 IS RECORD (z_id osoby.id%type, z_nazwisko osoby.nazwisko%type, z_imie osoby.imie1%type, z_data osoby.d_ur%type, z_plec osoby.plec%type ); z_osoba1 t_rekordosoba1; z_osoba2 t_rekordosoba2; -- podpunkt a SELECT id, nazwisko, imie1, d_ur, plec INTO z_osoba1 FROM osoby WHERE id = 1; DBMS_OUTPUT.PUT_LINE('Wybrano osobe o danych: id = ' z_osoba1.z_id ', ' z_osoba1.z_nazwisko ' ' z_osoba1.z_imie ', urodzona ' z_osoba1.z_data ' plec: ' z_osoba1.z_plec); -- podpunkt b SELECT id, nazwisko, imie1, d_ur, plec INTO z_osoba2 FROM osoby WHERE id = 1; DBMS_OUTPUT.PUT_LINE('Wybrano osobe o danych: id = ' z_osoba2.z_id ', ' z_osoba2.z_nazwisko ' ' z_osoba2.z_imie ', urodzona ' z_osoba2.z_data ' plec: ' z_osoba2.z_plec);
2. Zdefiniowac dwa typy rekordowe t_rekordosoba1, t_rekordosoba2 zawierajace nastepujace pola: z_id, z_nazwisko, z_imie, z_data_ur. Nastepnie zadeklarowac po dwie zmienne rekordowe dla kazdego typu. Niech pola jednej ze zmiennych jednego z typow rekordowych maja przypisane nastepujace wartosci: z_id = 20, z_nazwisko = 'Kowalska', z_imie = 'Maria', z_data_ur = 05/05/90 Wykonac operacje przypisania agregujacego do zmiennej drugiego typu oraz zmiennej tego samego typu. TYPE t_rekordosoba1 IS RECORD ( z_id osoby.id%type, z_nazwisko osoby.nazwisko%type, z_imie osoby.imie1%type, z_d_ur osoby.d_ur%type ); TYPE t_rekordosoba2 IS RECORD ( z_id osoby.id%type, z_nazwisko osoby.nazwisko%type, z_imie osoby.imie1%type, z_d_ur osoby.d_ur%type ); z_rekord1a t_rekordosoba1; z_rekord1b t_rekordosoba1; z_rekord2a t_rekordosoba2; z_rekord2b t_rekordosoba2; -- niestety przypisywanie danych trzeba robic po komorce... z_rekord1a.z_id := 20; z_rekord1a.z_nazwisko := 'Kowalska'; z_rekord1a.z_imie := 'Maria'; z_rekord1a.z_d_ur := TO_DATE('05/05/90','dd/mm/yyyy'); z_rekord1b := z_rekord1a; z_rekord2a.z_imie := z_rekord1a.z_imie; z_rekord2a.z_nazwisko := z_rekord1a.z_nazwisko; z_rekord2a.z_id := z_rekord1a.z_id; z_rekord2a.z_d_ur := z_rekord2a.z_d_ur; --z_rekord2b := z_rekord1a; --to wyzej NIGDY nie podziala
3. Wstawic do tabeli osoby nastepujacy rekord danych id = 15, nazwisko = Pawloska, imie Marta, data ur=23/05/82, plec=k. Wstawienie tego wiersza danych do tabeli bazodanowej przeprowadzic za pomoca zmiennej typu rekordowego. TYPE r_osoba IS RECORD (z_id osoby.id%type, z_nazwisko osoby.nazwisko%type, z_imie osoby.imie1%type, z_data osoby.d_ur%type, z_plec osoby.plec%type ); z_osoba r_osoba; z_osoba.z_id := 15; z_osoba.z_nazwisko := 'Pawloska'; z_osoba.z_imie := 'Marta'; z_osoba.z_data := TO_DATE('23/05/1982','dd/mm/yyyy'); z_osoba.z_plec := 'K'; INSERT INTO osoby VALUES (z_osoba.z_id, z_osoba.z_nazwisko, z_osoba.z_imie, NULL, z_osoba.z_data, z_osoba.z_plec); -- btw, dużo prościej byłoby użyć osoby%rowtype...
4. Napisac kod programu, ktory wykorzystuje identyfikator wiersza (ROWID). W tym celu zdefiniowac typ rekordowy dotyczacy cech osoby (oparty na tabeli osoby). Wstawic rekord danych do tabeli osoby: id = 30, nazwisko = 'Tomczyk', imie = 'Jan', d_ur = '21/01/80', plec = M. Po wstawieniu wydrukowac ROWID wstawianego rekordu: Identyfikator ROWID wstawianego wiersza to:... Nastepnie zaktualizowac rekord z tabeli osoby ktorego ROWID jest rowny ROWIDowi wstawianego rekordu. Przyklad: nazwisko='tomczak', imie='janusz'. Wyswietlic rekord przy pobraniu danych zaktualizowanego rekordu klauzula RETURNING... INTO... Nastepnie usunac wiersz z tabeli osoby, tej osoby ktorej id odpowiada aktualizowanego rekordu. Wydrukowac ROWID usuwanego rekordu: "Identyfikator usuwanego rekordu jest postaci..." z_rowid ROWID; TYPE r_osoba IS RECORD (z_id osoby.id%type, z_nazwisko osoby.nazwisko%type, z_imie osoby.imie1%type, z_data osoby.d_ur%type, z_plec osoby.plec%type ); z_osoba r_osoba; z_osoba.z_id := 30; z_osoba.z_nazwisko := 'Tomczyk'; z_osoba.z_imie := 'Jan'; z_osoba.z_data := TO_DATE('21/01/1980','dd/mm/yyyy'); z_osoba.z_plec := 'M'; INSERT INTO osoby VALUES (z_osoba.z_id, z_osoba.z_nazwisko, z_osoba.z_imie, NULL, z_osoba.z_data, z_osoba.z_plec) RETURNING ROWID INTO z_rowid; DBMS_OUTPUT.PUT_LINE('Identyfikator ROWID wstawianego wiersza to: ' z_rowid); UPDATE osoby SET imie1 = 'Janusz', nazwisko = 'Tomczak' WHERE ROWID = z_rowid RETURNING ROWID INTO z_rowid; DBMS_OUTPUT.PUT_LINE('Identyfikator ROWID zmienianego wiersza to: ' z_rowid);
DELETE osoby WHERE id = z_osoba.z_id RETURNING ROWID INTO z_rowid; DBMS_OUTPUT.PUT_LINE('Identyfikator ROWID usuwanego wiersza to: ' z_rowid); 5. Napisac kod programu, ktory wyswietli nastepujacy komunikat "Osoba wybrana, ktora jest Lis Jan ma inicjaly L.J." nalezy wykorzystac zmienne do podstawiania inicjalow, ktore sa typu zadeklarowanego przez uzytkownika jako typ tekstowy stalej dlugosci dlugosci 1. z_imie CHAR(1); z_nazwisko CHAR(1); z_imienazwisko VARCHAR2(50); SELECT imie1 ' ' nazwisko, SUBSTR(imie1,1,1), SUBSTR(nazwisko,1,1) INTO z_imienazwisko, z_imie, z_nazwisko FROM osoby WHERE id = 1; DBMS_OUTPUT.PUT_LINE('Osoba wybrana, ktora jest ' z_imienazwisko ' ma inicjaly' z_imie '.' z_nazwisko '.');
Ćwiczenia 4 instrukcje sterujące 1. Napisac kod bloku anonimowego w jezyku PL/SQL za pomoca ktorego dla danej osoby zostanie podany odpowiedni komunikat dotyczacy dlugosci jej imienia i nazwiska, a mianowicie gdy to suma dlugosci jest mniejsza od 8 to "Osoba o krotkiej dlugosci imienia i nazwiska, od 8 do 15 "osoba o duizej sumie dl imienia i nazwiska", zas powyzej o 15 "osoba o bardzo dluzej sumie dl imienia i nazwiska". z_imie osoby.imie1%type; z_nazwisko osoby.nazwisko%type; z_dlugosc NUMBER; SELECT o.imie1, o.nazwisko, LENGTH(o.imie1 ' ' o.nazwisko) INTO z_imie, z_nazwisko, z_dlugosc FROM osoby o WHERE o.id = 1; z_dlugosc := z_dlugosc - 1; IF z_dlugosc < 8 THEN DBMS_OUTPUT.PUT_LINE('Osoba o krotkiej dlugosci imienia i nazwiska'); ELSIF z_dlugosc >= 8 AND z_dlugosc <= 15 THEN DBMS_OUTPUT.PUT_LINE('Osoba o sredniej dlugosci imienia i nazwiska'); ELSE DBMS_OUTPUT.PUT_LINE('Osoba o duzej sumie dlugosci imienia i nazwiska'); END IF;
2. Napisac kod bloku anonimowego w jezyku PL/SQL w ktorym wybrana osoba bedzie przydzielona do odpowiedniej grupy wynagrodzen: 1 - ponizej 800zl 2-801-1100zl, 3-1101-1400zl 4-1401-1700zl 5-1701-2000zl 6-2001-2500zl 7-2501-3000zl itd co 500zl X - >6000zl użyc instrukcji CASE wyszukującej z_grupa NUMBER; z_id osoby.id%type; z_pensja pensje.pensja%type; SELECT o.id, p.pensja INTO z_id, z_pensja FROM osoby o, pensje p WHERE o.id = p.id_os AND p.do IS NULL AND o.id = 2; CASE WHEN z_pensja <= 800 THEN z_grupa := 1; WHEN z_pensja > 800 AND z_pensja <= 1100 THEN z_grupa := 2; WHEN z_pensja > 1100 AND z_pensja <= 1400 THEN z_grupa := 3; WHEN z_pensja > 1400 AND z_pensja <= 1700 THEN z_grupa := 4; WHEN z_pensja > 1700 AND z_pensja <= 2000 THEN z_grupa := 5; WHEN z_pensja > 2000 AND z_pensja <= 2500 THEN z_grupa := 6; WHEN z_pensja > 2500 AND z_pensja <= 3000 THEN z_grupa := 7; WHEN z_pensja > 3000 AND z_pensja <= 3500 THEN z_grupa := 8; WHEN z_pensja > 3500 AND z_pensja <= 4000 THEN z_grupa := 9; WHEN z_pensja > 4000 AND z_pensja <= 4500 THEN z_grupa := 10; WHEN z_pensja > 4500 AND z_pensja <= 5000 THEN z_grupa := 11; WHEN z_pensja > 5000 AND z_pensja <= 5500 THEN z_grupa := 12;
WHEN z_pensja > 5500 AND z_pensja <= 6000 THEN z_grupa := 12; WHEN z_pensja > 6000 THEN z_grupa := 13; END CASE; DBMS_OUTPUT.PUT_LINE('Osoba o id = ' z_id ', nalezy aktualnie do ' z_grupa ' grupy wynagrodzen, bo aktualnie zarabia ' z_pensja); 3. Napisac program, ktory bedzie podawal dla wybranej osoby komunikat w ktorym miesiacu sie urodziala np Osoba o id = 1 tj. Jan Lis urodzil sie w pazdzierniku. Zastosowac instrukcje CASE sprawdzajaca. z_imienazwisko VARCHAR2(50); z_msc VARCHAR2(2); z_mscslownie VARCHAR2(30); z_id osoby.id%type; SELECT id, imie1 ' ' nazwisko, TO_CHAR(d_ur,'MM') INTO z_id, z_imienazwisko, z_msc FROM osoby WHERE id = 1; CASE z_msc WHEN '01' THEN z_mscslownie := 'styczniu'; WHEN '02' THEN z_mscslownie := 'lutym'; WHEN '03' THEN z_mscslownie := 'marcu'; WHEN '04' THEN z_mscslownie := 'kwietniu'; WHEN '05' THEN z_mscslownie := 'maju'; WHEN '06' THEN z_mscslownie := 'czerwcu'; WHEN '07' THEN z_mscslownie := 'lipcu'; WHEN '08' THEN z_mscslownie := 'sierpniu'; WHEN '09' THEN z_mscslownie := 'wrzesniu'; WHEN '10' THEN z_mscslownie := 'pazdzierniku'; WHEN '11' THEN z_mscslownie := 'listopadzie'; WHEN '12' THEN z_mscslownie := 'grudniu'; ELSE z_mscslownie := 'blad!'; END CASE; DBMS_OUTPUT.PUT_LINE('Osoba o id = ' z_id ', tj. ' z_imienazwisko ' urodzila sie w ' z_mscslownie);
4. Napisac kod bloku anonimowego PL/sQL za pomoca ktorego bedzie wyswietlany dla osob o id od 1 do 10 komunikat: 1. Jan Lis ma inicjaly JL... z_imienazwisko VARCHAR2(50); z_inicjal1 CHAR(1); z_inicjal2 CHAR(2); FOR z_licznik IN 1..10 LOOP SELECT imie1 ' ' nazwisko, SUBSTR(imie1,1,1), SUBSTR(nazwisko,1,1) INTO z_imienazwisko, z_inicjal1, z_inicjal2 FROM osoby WHERE id = z_licznik; DBMS_OUTPUT.PUT_LINE(z_ImieNazwisko ' ma inicjaly ' z_inicjal1 '.' z_inicjal2 '.'); 5. Napisac kod bloku anonimowego z wykorzystaniem petli numerycznych FOR, za pomoca ktorego bedzie wyswietlona lista osob zatrudnionych na poszczegolnych wydzialach (zakladamy iz nie ma luk w numeracji id osob oraz sa trzy wydzialy i 10 osob) Trochę nieprecyzyjne polecenie, ponieważ należałoby jeszcze założyć, że wszystkie 10 osób aktualnie pracuje gdzieś (można to osiągnąć poniższą komendą). z_imienazwisko VARCHAR2(50); z_wydzial wydzialy.nazwa%type; z_wydzial_spr wydzialy.nazwa%type; z_licznik NUMBER; <<p_wydzialow>> FOR z_liczwydz IN 1..3 LOOP SELECT nazwa INTO z_wydzial FROM wydzialy WHERE id = z_liczwydz; DBMS_OUTPUT.PUT_LINE(z_Wydzial ' lista zatrudnionych'); z_licznik := 1;
<<p_osob>> FOR z_liczosob IN 1..10 LOOP SELECT w.nazwa, o.imie1 ' ' o.nazwisko INTO z_wydzial_spr, z_imienazwisko WHERE o.id = z.id_os AND w.id = z.id_wydz AND z.do IS NULL AND o.id = z_liczosob; IF z_wydzial_spr = z_wydzial THEN DBMS_OUTPUT.PUT_LINE(z_Licznik '. ' z_imienazwisko); z_licznik := z_licznik + 1; END IF; END LOOP p_osob; END LOOP p_wydzialow;
Ćwiczenia 5 kursory jawne 1. Napisac kod bloku anonimowego, ktory dla wszystkich osob wygeneruje komunikat: 1. Lis Jan pazdziernik 2. Kot Adam listopad... a) petla prosta b) petla WHILE -- podpunkt a CURSOR k_osoba IS SELECT imie1 ' ' nazwisko, TO_CHAR(d_ur,'MM') FROM osoby; z_imienazwisko VARCHAR2(50); z_licznik NUMBER := 1; z_msc VARCHAR2(2); z_mscslownie VARCHAR2(20); OPEN k_osoba; LOOP FETCH k_osoba INTO z_imienazwisko, z_msc; EXIT WHEN k_osoba%notfound; CASE z_msc WHEN '01' THEN z_mscslownie := 'styczniu'; WHEN '02' THEN z_mscslownie := 'lutym'; WHEN '03' THEN z_mscslownie := 'marcu'; WHEN '04' THEN z_mscslownie := 'kwietniu'; WHEN '05' THEN z_mscslownie := 'maju'; WHEN '06' THEN z_mscslownie := 'czerwcu'; WHEN '07' THEN z_mscslownie := 'lipcu'; WHEN '08' THEN z_mscslownie := 'sierpniu'; WHEN '09' THEN z_mscslownie := 'wrzesniu'; WHEN '10' THEN z_mscslownie := 'pazdzierniku'; WHEN '11' THEN z_mscslownie := 'listopadzie'; WHEN '12' THEN z_mscslownie := 'grudniu'; ELSE z_mscslownie := 'blad!'; END CASE; DBMS_OUTPUT.PUT_LINE(z_Licznik '. ' z_imienazwisko ', urodzony/a w ' z_mscslownie); z_licznik := z_licznik + 1; CLOSE k_osoba;
-- podpunkt b CURSOR k_osoba IS SELECT imie1 ' ' nazwisko, TO_CHAR(d_ur,'MM') FROM osoby; z_imienazwisko VARCHAR2(50); z_licznik NUMBER := 1; z_msc VARCHAR2(2); z_mscslownie VARCHAR2(20); OPEN k_osoba; FETCH k_osoba INTO z_imienazwisko, z_msc; WHILE k_osoba%found LOOP CASE z_msc WHEN '01' THEN z_mscslownie := 'styczniu'; WHEN '02' THEN z_mscslownie := 'lutym'; WHEN '03' THEN z_mscslownie := 'marcu'; WHEN '04' THEN z_mscslownie := 'kwietniu'; WHEN '05' THEN z_mscslownie := 'maju'; WHEN '06' THEN z_mscslownie := 'czerwcu'; WHEN '07' THEN z_mscslownie := 'lipcu'; WHEN '08' THEN z_mscslownie := 'sierpniu'; WHEN '09' THEN z_mscslownie := 'wrzesniu'; WHEN '10' THEN z_mscslownie := 'pazdzierniku'; WHEN '11' THEN z_mscslownie := 'listopadzie'; WHEN '12' THEN z_mscslownie := 'grudniu'; ELSE z_mscslownie := 'blad!'; END CASE; DBMS_OUTPUT.PUT_LINE(z_Licznik '. ' z_imienazwisko ', urodzony/a w ' z_mscslownie); z_licznik := z_licznik + 1; FETCH k_osoba INTO z_imienazwisko, z_msc; CLOSE k_osoba;
2. Napisac kod bloku anonimowego PL/SQL z kursorem jawnym umozliwiajacy wyswietlanie dla poszczegolnych plci osob zatrudnionych w poszczegolnych miesiacach K styczen 1 K marzec 4 CURSOR k_osoba IS SELECT o.plec, TO_CHAR(o.d_ur,'MM') miesiac, COUNT(o.id) ilosc FROM osoby o GROUP BY o.plec, TO_CHAR(o.d_ur,'MM') ORDER BY o.plec, TO_CHAR(o.d_ur,'MM'); z_mscslownie VARCHAR2(20); FOR z_tmp IN k_osoba LOOP CASE z_tmp.miesiac WHEN '01' THEN z_mscslownie := 'styczen'; WHEN '02' THEN z_mscslownie := 'luty'; WHEN '03' THEN z_mscslownie := 'marzec'; WHEN '04' THEN z_mscslownie := 'kwiecien'; WHEN '05' THEN z_mscslownie := 'maj'; WHEN '06' THEN z_mscslownie := 'czerwiec'; WHEN '07' THEN z_mscslownie := 'lipiec'; WHEN '08' THEN z_mscslownie := 'sierpien'; WHEN '09' THEN z_mscslownie := 'wrzesien'; WHEN '10' THEN z_mscslownie := 'pazdziernik'; WHEN '11' THEN z_mscslownie := 'listopad'; WHEN '12' THEN z_mscslownie := 'grudzien'; ELSE z_mscslownie := 'blad!'; END CASE; DBMS_OUTPUT.PUT_LINE(z_Tmp.plec ' ' z_mscslownie ' ' z_tmp.ilosc);
3. Zaprojektowac kursor jawny z blokada, ktorego zadaniem jest podniesienie aktualnej pensji wszystkim kobietom o 10%. Transakcje zatwierdzic. CURSOR k_podniespensjekobietom IS SELECT o.id, p.pensja FROM osoby o, pensje p WHERE o.plec = 'K' AND o.id = p.id_os AND p.do IS NULL FOR UPDATE OF p.pensja; FOR z_tmp IN k_podniespensjekobietom LOOP UPDATE pensje SET pensja = pensja * 1.1 WHERE CURRENT OF k_podniespensjekobietom; COMMIT; 4. Mamy 1000zl na podwyzke akltualnej pensji dla wszystkich aktualnie zatrudnionych osob. Zaprojketowac kursor jawny z blokada ktorego zadaniem jest podniesienie aktualnej pensji wszystkim osobom aktuyalnie zatrudnionym o te sama stawke. Zatwierdzic tranzakcje. CURSOR k_podniespensje IS SELECT o.id, p.pensja FROM osoby o, pensje p, zatrudnienia z WHERE o.id = z.id_os AND o.id = p.id_os AND z.do IS NULL AND p.do IS NULL FOR UPDATE OF p.pensja; z_iloscosob NUMBER; Osob FROM osoby o, pensje p, zatrudnienia z WHERE o.id = z.id_os AND o.id = p.id_os AND z.do IS NULL AND p.do IS NULL; FOR z_tmp IN k_podniespensje LOOP UPDATE pensje SET pensja = pensja + (1000 / z_iloscosob); WHERE CURRENT OF k_podniespensje; COMMIT;
Ćwiczenia 6 obsługa wyjątków 1. Mamy dokonac podwyzki aktualnej pensji wszystkim aktualnie zatrudnionym osobom kazda z nich ma dostac taka kwote podwyzki jaka wynika z ostatniego okresu pracy na danym wydziale (za kazdy pelny przepracowany rok osoba dostaje podwyzke w wysokosci 3,5% aktualnej pensji. Zaprojektowac kursor jawny z blokada przy pomocy ktorego zostanie zrealizowana powyzsza tranzakcja. Tranzjakcje zatwierdzic. CURSOR k_podnies IS SELECT TRUNC(MONTHS_BETWEEN(p.od,SYSDATE)/12,0) ile, p.pensja FROM pensje p, osoby o, zatrudnienia z WHERE o.id = z.id_os AND o.id = p.id_os AND p.do IS NULL AND z.do IS NULL FOR UPDATE OF p.pensja; FOR z_tmp IN k_podnies LOOP UPDATE pensje SET pensja = pensja + (z_tmp.ile * 0.035) * pensja WHERE CURRENT OF k_podnies; COMMIT; 2. W bloku anonimowym PL/SQL zdefiniowano zmienna z_nazwisko, typu VARCHAR2(10), zas w sekcji wykonania tego bloku przypisano wartosc 'Nowak - Konopacka'. Zaprojektowac obsluge tego wyjatku poprzez wyswietlenie komunikatu: 'Zbyt dlugi ciag znakow alfanumerycznych'. z_nazwisko VARCHAR2(10); z_nazwisko := 'Nowak Konopacka'; EXCEPTION WHEN value_error THEN DBMS_OUTPUT.PUT_LINE('Zbyt dlugi ciag znakow alfanumerycznych'); /* lub RAISE_APPLICATION_ERROR(-20000,'Zbyt dlugi ciag znakow alfanumerycznych'); */
3. Napisac kod bloku anonimowego anonimowego PL/SQL za pomoca ktorego zostana wyswietlone te aktualnie zatrudnione osoby, ktore przepracowaly wiecej niz x lat pracy na danym wydziale (x jest liczba calkowita zadana poprzez wartosc zmiennej PL/SQL). Zaprojektowac obsluge wszystkich mozliwych wyjatkow jakie moga wystapic przy zalozeniu iz w bloku wykorzystano kursor niejawny. z_latapracy NUMBER(2); z_osoba osoby%rowtype; z_wydzial wydzialy.nazwa%type; -- przykladowe dane z_latapracy := 2; z_wydzial := 'Fizyka'; SELECT o.* INTO z_osoba FROM osoby o, zatrudnienia z, wydzialy w WHERE o.id = z.id_os AND z.do IS NULL AND z.id_wydz = w.id AND w.nazwa = z_wydzial AND TRUNC(MONTHS_BETWEEN(z.od,SYSDATE)/12,0) >= z_latapracy; DBMS_OUTPUT.PUT_LINE(z_Osoba.id ' ' z_osoba.imie1 ' ' z_osoba.nazwisko ' ' z_osoba.d_ur ' ' z_osoba.plec); EXCEPTION WHEN value_error THEN RAISE_APPLICATION_ERROR(-20000, 'Nieprawidlowe kryteria!'); -- np. podanie, ze ktos przepracowal 1000 lat albo za dluga nazwa wydzialu WHEN no_data_found THEN RAISE_APPLICATION_ERROR(-20001, 'Nie znaleziono osoby!'); WHEN too_many_rows THEN RAISE_APPLICATION_ERROR(-20002, 'Wybrano wiecej, niz jedna osobe!'); WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20003, 'Nieznany blad!');
4. Do tabeli osoby i zatrudniono wprowadzono osobe o id = 11, ktora jak sie okazalo ma niepoprawne dane, gdyz jej data urodzila jest pozniejsza niz zatrudnienia i/lub data zatrudnienia jest pozniejsza niz data zwolnienia. Nalezy zaprojektowac obsluge wyjatkow, ktore wylapuja taka sytuacje dla dowolonej osoby. Obsluga wyjatkow ma polegac na wylapaniu i obsluzeniu nastepujacych bledow: a) data urodzenia jest pozniejsza niz data zatrudnienia b) data zatrudnienia jest pozniejsza niz zwolnienia c) data urodzenia jest pozniejsza niz data zatrudnienia oraz data zatrudnienia jest pozniejsza niz zwolnienia -- przykladowe dane wywolujace pierwszy wyjatek INSERT INTO osoby VALUES (11,'Pazdzioch','Marian',NULL,TO_DATE('2009/01/01','yyyy/mm/dd'),'M'); INSERT INTO ZATRUDNIENIA VALUES (11,11,TO_DATE('2008/02/01','yyyy/mm/dd'),TO_DATE('2007/01/04','yyyy/mm/dd'),2); -- rozwiazanie z_dataurodzenia DATE; z_datazatrudnienia DATE; z_datazwolnienia DATE; z_id osoby.id%type := 11; w_dataurodzeniapozniejsza EXCEPTION; w_datazatrudnieniapozniejsza EXCEPTION; w_obiedatyzle EXCEPTION; SELECT o.d_ur, z.od, z.do INTO z_dataurodzenia, z_datazatrudnienia, z_datazwolnienia FROM osoby o, zatrudnienia z WHERE o.id = z_id AND o.id = z.id_os; IF z_dataurodzenia > z_datazatrudnienia AND z_datazatrudnienia > z_datazwolnienia THEN RAISE w_obiedatyzle; ELSIF z_dataurodzenia > z_datazatrudnienia THEN RAISE w_dataurodzeniapozniejsza; ELSIF z_datazatrudnienia > z_datazwolnienia THEN RAISE w_datazatrudnieniapozniejsza; END IF; EXCEPTION WHEN w_obiedatyzle THEN RAISE_APPLICATION_ERROR(-20000,'Data urodzenia pozniejsza niz data zatrudnienia oraz data zatrudnienia pozniejsza niz data zwolnienia'); WHEN w_dataurodzeniapozniejsza THEN RAISE_APPLICATION_ERROR(-20001,'Data urodzenia pozniejsza niz data zatrudnienia'); WHEN w_datazatrudnieniapozniejsza THEN
RAISE_APPLICATION_ERROR(-20002,'Data zatrudnienia jest pozniejsza niz data zwolnienia');
Ćwiczenia 7 funkcje i procedury 1. Wykorzystac odpowiednie funkcje do zapisania kodu bledu komunikatu o tym bledzie, w przypadku zastosowania wyjatku OTHERS do obslugi tego bledu. Kod bledu i komunikat zapisac w tabeli tabela_bledow (kod bledu, komunikat bledow). Przykladowy blad, ktory obsluguje ten wyjatek OTHERS to pobranie kursorem niejawnym danych osoby o id = 50. DROP TABLE tabela_bledow; CREATE TABLE tabela_bledow ( kod_bledu NUMBER, komunikat_bledu VARCHAR2(200)); z_imienazwisko VARCHAR2(50); z_nrbledu tabela_bledow.kod_bledu%type; z_komunikatbledu tabela_bledow.komunikat_bledu%type; SELECT imie1 ' ' nazwisko INTO z_imienazwisko FROM osoby WHERE id = 50; EXCEPTION WHEN OTHERS THEN z_nrbledu := SQLCODE; z_komunikatbledu := SUBSTR(SQLERRM,1,200); INSERT INTO tabela_bledow VALUES (z_nrbledu,z_komunikatbledu);
2. a) Zaprojektowac procedure do wstawiania rekordow do tabeli osoby. b) obsluzyc wyjatek polegajacy na wprowadzeniu osoby o istniejacym id CREATE OR REPLACE PROCEDURE DodajOsobe (p_id osoby.id%type, p_nazwisko osoby.nazwisko%type, p_imie1 osoby.imie1%type, p_imie2 osoby.imie2%type, p_dataur DATE, p_plec osoby.plec%type) AS INSERT INTO osoby VALUES (p_id,p_nazwisko,p_imie1,p_imie2,p_dataur,p_plec); EXCEPTION WHEN dup_val_on_index THEN RAISE_APPLICATION_ERROR(-20000,'Osoba o podanym id istnieje!'); -- wywolanie EXECUTE DodajOsobe (1, 'Jozwiak', 'Marek', 'Janusz', TO_DATE('02/08/1962','dd/mm/yyyy'),'M'); 3. Zaprojektowac procedure i funkcje obliczania liczby wszystkich kobiet lub mezczyzn. Nastepnie wywolac je za pomoca instrukcji SQLowej i wydrukowac wynik. CREATE OR REPLACE FUNCTION IleOsob (p_plec osoby.plec%type) RETURN NUMBER AS z_ilosc NUMBER; SELECT COUNT(id) FROM osoby WHERE plec = p_plec; RETURN z_ilosc; -- wywolanie VARIABLE Ilosc NUMBER; EXECUTE :Ilosc := IleOsob('M'); -- lub -- CALL IleOsob('M') INTO :Ilosc; PRINT Ilosc;
4. Zaprojektowac funkcje ktora dla kazdego wyzialu poda nam w formie komunikatu informacje o procencie zatrudnionych aktualnie na nim osob w stosunku do liczby wszystkich aktualnie zatrudnionych osob. KOmunikaty: Wydzial zatrudnia ponad 50% aktualnie zatrudnionych osob. Wydzial zatrudnia od 25% do 50% aktualnie zatrudnionych osob. Wydzial zatrudnia ponizej 25% aktualnie zatrudnionych osob. Na wydziale nikt nie jest zatrudniony. UWAGA! Zastosowac instrukcje z wiecej niz jedna klauzula RETURN; CREATE OR REPLACE FUNCTION DajProcentZatrudnionych (p_nazwa wydzialy.nazwa%type) RETURN VARCHAR2 AS z_iloscwszystkich NUMBER; z_iloscnawydziale NUMBER; z_procent NUMBER; Wszystkich FROM osoby o, zatrudnienia z WHERE o.id = z.id_os AND z.do IS NULL; NaWydziale FROM osoby o, zatrudnienia z, wydzialy w WHERE o.id = z.id_os AND z.do IS NULL AND w.id = z.id_wydz AND INITCAP(w.nazwa) = INITCAP(p_Nazwa); z_procent := (z_iloscnawydziale / z_iloscwszystkich) * 100; IF z_procent > 50 THEN RETURN 'Wydzial zatrudnia ponad 50% aktualnie zatrudnionych osob'; ELSIF z_procent >= 25 AND z_procent <= 50 THEN RETURN 'Wydzial zatrudnia od 25% do 50% aktualnie zatrudnionych osob'; ELSE RETURN 'Wydzial zatrudnia ponizej 25% aktualnie zatrudnionych osob'; END IF; -- wywolanie VARIABLE Tekst VARCHAR2(100); EXECUTE :Tekst := DajProcentZatrudnionych('matematyka'); PRINT Tekst;
5. Zaprojektowac blok anonimowy PL/SQL za pomoca ktorego zostanie wyswietlona dla wszystkich liter alfabetu liczba osob na poszczegolnych wydzialach o imieniu na dana litere. Zaprojektowac przypadek braku osoby na wydziale o imieniu na dana litere na wyswietlenie 0. -- moze da sie to rozwiazac latwiej, niestety nie da sie zrobic petli po literach alfabetu, takze --- nie wiem jakby to mialo wygladac -- moznaby ewentualnie stworzyc funkcje, aby oszczedzic na pisaniu... kursora jawnego chyba nie da sie uzyc... z_litera CHAR(1); z_ilosc NUMBER; z_wydzial wydzialy.nazwa%type; CURSOR k_wydzialy IS SELECT nazwa FROM wydzialy; FOR z_tmp IN k_wydzialy LOOP z_wydzial := z_tmp.nazwa; DBMS_OUTPUT.PUT_LINE('Wydzial: ' INITCAP(z_Wydzial)); z_litera := 'a'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'b'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'c'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial;
DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'd'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'e'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'f'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'g'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'h'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'i';
WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'j'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'k'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'l'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'm'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'n'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc);
z_litera := 'o'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'p'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'r'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 's'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 't'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'q';
WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'u'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'w'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'x'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'y'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); z_litera := 'z'; WHERE UPPER(SUBSTR(o.imie1,1,1)) = UPPER(z_Litera) AND z.do IS NULL AND o.id = z.id_os AND w.id = z.id_wydz AND w.nazwa = z_wydzial; DBMS_OUTPUT.PUT_LINE('Litera: ' z_litera ', osob: ' z_ilosc); DBMS_OUTPUT.PUT_LINE('====================');
Zajęcia 8 kolekcje 1. Zadeklarowac 3 tabele indeksowane, do ktorych wprowadzic odpowiednio: nazwiska, imiona i daty urodzin osob z tabeli osoby, przy czym kolejne indeksy elementow tych tabel maja byc nieujemnymi liczbami calkowitymi podzielnymi przez 5, a ponadto osoby maja byc wprowadzone do tabel alfabetycznie (nazwiska i imiona) - wydrukowac elementy tych tabel bezposrednio po wprowadzeniu do nich danych. TYPE t_nazwiska IS TABLE OF osoby.nazwisko%type INDEX BY BINARY_INTEGER; TYPE t_imiona IS TABLE OF osoby.imie1%type INDEX BY BINARY_INTEGER; TYPE t_datyur IS TABLE OF DATE INDEX BY BINARY_INTEGER; z_licznik NUMBER := 0; z_nazwiska t_nazwiska; z_imiona t_imiona; z_datyur t_datyur; CURSOR k_osoba IS SELECT nazwisko, imie1, d_ur FROM osoby ORDER BY nazwisko, imie1; FOR z_tmp IN k_osoba LOOP z_nazwiska(z_licznik) := z_tmp.nazwisko; z_imiona(z_licznik) := z_tmp.imie1; z_datyur(z_licznik) := z_tmp.d_ur; DBMS_OUTPUT.PUT_LINE(z_Licznik ': ' z_nazwiska(z_licznik) ' ' z_imiona(z_licznik) ' ' z_datyur(z_licznik)); z_licznik := z_licznik + 5;
2. Przypisując w ten sam sposób dane z tabeli osoby do tabel indeksowanych oraz nie znajac liczby osob, ktorych dane zostaly wprowadzone do tabel indeksowanych wydrukowac w innej petli niz wprowadzenie danych, a mianowicie w petli FOR elementy tabel indeksowanych. UWAGA - nie liczymy elementow w tabeli osoby, ale elementy tabel indeksowanych. TYPE t_nazwiska IS TABLE OF osoby.nazwisko%type INDEX BY BINARY_INTEGER; TYPE t_imiona IS TABLE OF osoby.imie1%type INDEX BY BINARY_INTEGER; TYPE t_datyur IS TABLE OF DATE INDEX BY BINARY_INTEGER; z_licznik NUMBER := 0; z_nazwiska t_nazwiska; z_imiona t_imiona; z_datyur t_datyur; CURSOR k_osoba IS SELECT nazwisko, imie1, d_ur FROM osoby ORDER BY nazwisko, imie1; FOR z_tmp IN k_osoba LOOP z_nazwiska(z_licznik) := z_tmp.nazwisko; z_imiona(z_licznik) := z_tmp.imie1; z_datyur(z_licznik) := z_tmp.d_ur; z_licznik := z_licznik + 5; FOR i IN 0..z_Nazwiska.COUNT-1 LOOP z_licznik := i * 5; DBMS_OUTPUT.PUT_LINE(z_Licznik ': ' z_nazwiska(z_licznik) ' ' z_imiona(z_licznik) ' ' z_datyur(z_licznik));
3. Przypisac nazwiska i imiona z tabeli osoby do elementow tabeli indeksowanej w ten sposob, iz indeksy sa nieujemnymi liczbami calkowitymi podzielnymi przez 3. Wydrukowac te elementy w petli FOR nie znajac ich liczby. Nastepnie usunac te elementz tabeli nieindeksowanej ktorzch indeks jest podyielnz prey 6 i powtornie wzdrukowac te elementy przy wykorzystaniu petli FOR. z_licznik NUMBER := 0; TYPE t_nazwiska IS TABLE OF osoby.nazwisko%type INDEX BY BINARY_INTEGER; TYPE t_imiona IS TABLE OF osoby.imie1%type INDEX BY BINARY_INTEGER; z_nazwiska t_nazwiska; z_imiona t_imiona; CURSOR k_osoba IS SELECT nazwisko, imie1 FROM osoby; FOR z_tmp IN k_osoba LOOP z_nazwiska(z_licznik) := z_tmp.nazwisko; z_imiona(z_licznik) := z_tmp.imie1; z_licznik := z_licznik + 3; FOR i IN 0..z_Nazwiska.COUNT-1 LOOP z_licznik := i * 3; DBMS_OUTPUT.PUT_LINE(z_Licznik ': ' z_nazwiska(z_licznik) ' ' z_imiona(z_licznik)); FOR i IN 0..z_Nazwiska.COUNT-1 LOOP z_licznik := i * 3; IF MOD(z_Licznik,6) = 0 THEN z_nazwiska.delete(z_licznik); z_imiona.delete(z_licznik); END IF;
DBMS_OUTPUT.PUT_LINE('Po usunieciu:'); z_licznik := 3; FOR i IN 0..z_Nazwiska.COUNT-1 LOOP z_licznik := 3 + i * 6; DBMS_OUTPUT.PUT_LINE(z_Licznik ': ' z_nazwiska(z_licznik) ' ' z_imiona(z_licznik)); -- moznaby tez sprobowac cos zdzialac przy uzyciu metody NEXT 4. Utworzyc kolekcje typu tabela zagniezdzona, ktorych elementami sa liczby. Zainicjowac zmienna tego typu liczbami pierwszymi niewiekszymi niz 20. Nastepnie wydrukowac te elementy tej tabeli zagniezdzonej, ktore przy dzieleniu przez 3 daja reszte 1. Przy zalozeniu ze nie jest znana liczba tych elementow. TYPE t_liczby IS TABLE OF NUMBER; z_liczby t_liczby := t_liczby(2,3,5,7,11,13,17,19); FOR i IN 1..z_Liczby.COUNT LOOP IF MOD(i,3) = 1 THEN DBMS_OUTPUT.PUT_LINE(i ': ' z_liczby(i)); END IF;
5. Utworzyc kolekcje typu tabela o zmiennym rozmiarze, ktorej elementami sa liczby. Nastepnie zainicjowac zmienna tego typu liczbami pierwszymi niewiekszymi od 20. Obliczyc srednia arytmenyczna elementow tej kolekcji. Wybrac i wydrukowac te elementy z tabeli o zmiennym rozmiarze, ktorych wartosc jest wieksza niz dotychczasowa srednia. 6. W zadaniu 5 dopisac linie kodu pozwalajace wydrukowac aktualna liczbe elementow kolekcji oraz maksymalna liczbe elementow kolekcji. Wykonac polecenia jak w zadaniu 5. Nastepnie rozszerzyc kolekcje o 3 duplikaty trzeciego elementu kolekcji i powtorzyc obliczenia jak w zadaniu 5. TYPE t_liczby IS VARRAY(20) OF NUMBER; z_liczby t_liczby := t_liczby(2,3,5,7,11,13,17,19); z_srednia NUMBER; z_suma NUMBER := 0; FOR i IN 1..z_Liczby.COUNT LOOP z_suma := z_suma + z_liczby(i); z_srednia := z_suma / z_liczby.count; DBMS_OUTPUT.PUT_LINE('Srednia: ' TRUNC(z_Srednia,2)); DBMS_OUTPUT.PUT_LINE('Elementy wieksze niz srednia:'); FOR i IN 1..z_Liczby.COUNT LOOP IF z_liczby(i) > z_srednia THEN DBMS_OUTPUT.PUT_LINE(z_Liczby(i)); END IF; -- zadanie 6 DBMS_OUTPUT.PUT_LINE('============='); DBMS_OUTPUT.PUT_LINE('Ilosc elementow: ' z_liczby.count); DBMS_OUTPUT.PUT_LINE('Maksymalny rozmiar: ' z_liczby.limit); FOR i IN 1..3 LOOP z_liczby.ext z_liczby(z_liczby.last) := z_liczby(3); z_suma := 0; FOR i IN 1..z_Liczby.COUNT LOOP z_suma := z_suma + z_liczby(i); z_srednia := z_suma / z_liczby.count; DBMS_OUTPUT.PUT_LINE('Nowa srednia: ' TRUNC(z_Srednia,2));
Ćwiczenia 9 kolekcje, ciąg dalszy Zadanie 27 z http://math.uni.lodz.pl/~antczak/plsql/zadaniasprawdzian.pdf ponadto: 1. Utworzyc klucz zlozony w tabeli wydzialy1 skladajacy sie z pol nazwa i kierunek, 2. Utworzyc klucz zlozony w tabeli WYKAZ_EGZAMINOW skladajacy sie z pol KOD_P i DATA_EGZ -- zmienilem nazwe z WYKAZ_EGZAMINOW na TABELA_EGZAMINOW, poniewaz byla taka sama jak kolumna w tabeli wydzialy, co koliduje z moją moralnościa ;) -- TABELE DROP TABLE przedmioty; DROP TABLE studenci; DROP TABLE wydzialy2; DROP TABLE tabela_egzaminow; -- klauzule NOT NULL pomijamy, bo nie ma o niej mowy w poleceniu CREATE TABLE przedmioty ( kod_p NUMBER(2) PRIMARY KEY, nazwa VARCHAR2(30), l_godz_wykl NUMBER(2), l_godz_cw NUMBER(2)); CREATE TABLE studenci ( id NUMBER(2) PRIMARY KEY, nazwisko VARCHAR2(20), imie VARCHAR2(20)); CREATE OR REPLACE TYPE t_wykaz_egzaminow AS VARRAY(20) OF NUMBER(2); / CREATE TABLE wydzialy2 ( nazwa VARCHAR2(20), kierunek VARCHAR2(20), wykaz_egzaminow t_wykaz_egzaminow, CONSTRAINT pk_wydzialy2 PRIMARY KEY (nazwa, kierunek)); CREATE OR REPLACE TYPE t_zdawany_przez AS TABLE OF NUMBER(2); / CREATE TABLE tabela_egzaminow ( kod_p NUMBER(2), data_egz DATE, zdawany_przez t_zdawany_przez, CONSTRAINT pk_tabela_egzaminow PRIMARY KEY(kod_p,data_egz)) NESTED TABLE zdawany_przez STORE AS st_tab; INSERT INTO przedmioty VALUES (10,'Analiza matematyczna',4,4); INSERT INTO przedmioty VALUES (20,'Bazy danych',2,2); INSERT INTO przedmioty VALUES (30,'Historia starozytna',3,0); INSERT INTO przedmioty VALUES (40,'Algebra',4,2);
INSERT INTO przedmioty VALUES (50,'Metody numeryczne',2,2); INSERT INTO przedmioty VALUES (60,'Historia Polski',4,0); INSERT INTO przedmioty VALUES (70,'Funkcje rzeczywiste',4,2); INSERT INTO studenci VALUES (1,'Nowak','Jerzy'); INSERT INTO studenci VALUES (2,'Kraska','Adam'); INSERT INTO studenci VALUES (3,'Rosiak','Monika'); INSERT INTO studenci VALUES (4,'Czaplinski','Mateusz'); INSERT INTO studenci VALUES (5,'Adamczyk','Elzbieta'); INSERT INTO studenci VALUES (6,'Daniec','Katarzyna'); INSERT INTO studenci VALUES (7,'Kraczycki','Piotr'); INSERT INTO wydzialy2 VALUES ('Matematyki', 'Matematyka', t_wykaz_egzaminow(10,40,70)); INSERT INTO wydzialy2 VALUES ('Historii', 'Dydaktyka historii', t_wykaz_egzaminow(30,60)); INSERT INTO wydzialy2 VALUES ('Matematyki', 'Informatyka', t_wykaz_egzaminow(20,50)); INSERT INTO tabela_egzaminow VALUES (10,TO_DATE('23/01/2001','dd/mm/yyyy'), t_zdawany_przez(1,3,5)); INSERT INTO tabela_egzaminow VALUES (20,TO_DATE('25/06/2001','dd/mm/yyyy'), t_zdawany_przez(4,7)); INSERT INTO tabela_egzaminow VALUES (30,TO_DATE('15/01/2001','dd/mm/yyyy'), t_zdawany_przez(2,6)); INSERT INTO tabela_egzaminow VALUES (40,TO_DATE('20/01/2001','dd/mm/yyyy'), t_zdawany_przez(1,3,5)); INSERT INTO tabela_egzaminow VALUES (50,TO_DATE('21/01/2001','dd/mm/yyyy'), t_zdawany_przez(4,7)); INSERT INTO tabela_egzaminow VALUES (60,TO_DATE('19/01/2001','dd/mm/yyyy'), t_zdawany_przez(2,6)); INSERT INTO tabela_egzaminow VALUES (70,TO_DATE('29/01/2001','dd/mm/yyyy'), t_zdawany_przez(3,5)); -- PROCEDURY CREATE OR REPLACE PROCEDURE lista_egzaminow AS CURSOR k_wydzialy IS SELECT nazwa, kierunek FROM wydzialy2; CURSOR k_egzaminy IS SELECT kod_p, nazwa FROM przedmioty; z_wykaz t_wykaz_egzaminow; z_kodprzedmiotu przedmioty.kod_p%type; z_nazwaprzedmiotu przedmioty.nazwa%type; <<petla_wydzialow>>
FOR z_tmpw IN k_wydzialy LOOP DBMS_OUTPUT.PUT_LINE('Egzaminy na wydziale ' z_tmpw.nazwa ' dla kierunku ' z_tmpw.kierunek ':'); <<petla_egzaminow>> FOR z_tmpegz IN k_egzaminy LOOP SELECT wykaz_egzaminow INTO z_wykaz FROM wydzialy2 WHERE nazwa = z_tmpw.nazwa AND kierunek = z_tmpw.kierunek; / FOR i IN 1..z_Wykaz.COUNT LOOP IF z_tmpegz.kod_p = z_wykaz(i) THEN DBMS_OUTPUT.PUT_LINE(z_TmpEgz.kod_p ' ' z_tmpegz.nazwa); END IF; END LOOP petla_egzaminow; END LOOP petla_wydzialow; CREATE OR REPLACE PROCEDURE lista_zdajacych (p_nazwa przedmioty.nazwa%type) AS z_zdajacy t_zdawany_przez; z_imienazwisko VARCHAR2(50); SELECT e.zdawany_przez INTO z_zdajacy FROM tabela_egzaminow e, przedmioty p WHERE e.kod_p = p.kod_p AND p.nazwa = p_nazwa; DBMS_OUTPUT.PUT_LINE('Osoby zdajace egzamin z przedmiotu ' p_nazwa ': '); FOR i IN 1..z_Zdajacy.COUNT LOOP SELECT nazwisko ' ' imie INTO z_imienazwisko FROM studenci WHERE id = z_zdajacy(i); / DBMS_OUTPUT.PUT_LINE(i '. ' z_imienazwisko); -- SPRAWDZENIE DZIALANIA CALL lista_egzaminow(); CALL lista_zdajacych('bazy danych');
UWAGI: 1. uzywajac CALL nawiasy sa konieczne, nawet jezeli wywolywana funkcja nie przyjmuje zadnych argumentow 2. jeżeli w poleceniu jest napisane, aby wywolac funkcje/procedure poleceniem SQL-owym, to uzywamy funkcji CALL, poniewaz funkcja EXECUTE jest funkcja srodowiska isqlplus, a nie samego języka
Ćwiczenia 10 pakiety 1. Zaprojektowac pakiet zawierajacy procedure wprowadzenia nowej osoby do tabeli osoby, procedure usuwania osoby juz wprowadzonej uprzednio do tabeli osoby, przy czym musi ona zawierac obsluge deklarowanego przez uzytkownika bledu nie znalezienia rekordu do usuniecia oraz procedure wyswietajaca liste aktualnie zatrudnionej osoby na wskazanym wydziale przy czym lista ta powinna byc zwracana przy wykorzystaniu odpowiedniego typu kolekcji (typow kolekcji). 2. Rozszerzyc pakiet z zadania 1 w ten sposob iz bedzie on zawieral procedure widoczna w obrebie samego pakietu ktora to procedura bedzie aktualizowala tabele plec_osob (plec, liczba_osob) ktora trzeba bedzie utworzyc w ten sposob, ze zawiera ona nazwe plci i liczbe osob danej plci. po kazdej operacji ustawienia badz usuniecia osoby z tabeli osoby za pomoca procedur z zadania 1. CREATE OR REPLACE PACKAGE p_osoby AS PROCEDURE DodajOsobe (p_osoba osoby%rowtype); PROCEDURE UsunOsobe (p_id osoby.id%type); TYPE t_osobyzatrudnione IS TABLE OF osoby%rowtype INDEX BY BINARY_INTEGER; PROCEDURE WypiszZatrudnionych (p_nazwa IN wydzialy.nazwa%type, p_osoby OUT t_osobyzatrudnione); END p_osoby; / -- tabela do zadania 2 CREATE TABLE plec_osob AS SELECT plec, COUNT(plec) ilosc FROM osoby ORDER BY plec; CREATE OR REPLACE PACKAGE BODY p_osoby AS -- procedura do zadania 2 PROCEDURE AktualizujPlecOsob AS CURSOR k_plec IS SELECT plec, COUNT(plec) ilosc FROM osoby GROUP BY plec; FOR z_tmp IN k_plec LOOP UPDATE plec_osob SET ilosc = z_tmp.ilosc WHERE plec = z_tmp.plec; PROCEDURE DodajOsobe (p_osoba osoby%rowtype) AS
INSERT INTO osoby VALUES (p_osoba.id, p_osoba.nazwisko, p_osoba.imie1, p_osoba.imie2, p_osoba.d_ur, p_osoba.plec); -- z jakiegos powodu nie da sie wstawic od razu calego rekordu... -- akutalizacja tabeli z zadania 2 AktualizujPlecOsob; END DodajOsobe; PROCEDURE UsunOsobe (p_id osoby.id%type) AS w_brakosoby EXCEPTION; z_id osoby.id%type; SELECT id INTO z_id FROM osoby WHERE id = p_id; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE w_brakosoby; DELETE osoby WHERE id = p_id; DBMS_OUTPUT.PUT_LINE('Osoba o id = ' p_id ' zostala usunieta z tabeli osoby.'); -- aktualizacja tabeli z zadania 2 AktualizujPlecOsob; EXCEPTION WHEN w_brakosoby THEN DBMS_OUTPUT.PUT_LINE('Nie znaleziono osoby do usuniecia!'); PROCEDURE WypiszZatrudnionych (p_nazwa IN wydzialy.nazwa%type, p_osoby OUT t_osobyzatrudnione) AS z_liczbaosob NUMBER; CURSOR k_osoba IS SELECT o.id, w.nazwa WHERE o.id = z.id_os AND w.id = z.id_wydz AND z.do IS NULL; z_licznik NUMBER := 1; INTO z_liczbaosob
WHERE o.id = z.id_os AND w.id = z.id_wydz AND z.do IS NULL AND w.nazwa = p_nazwa; DBMS_OUTPUT.PUT_LINE('Osoby zatrudnione na wydziale ' p_nazwa); FOR z_tmp IN k_osoba LOOP IF z_tmp.nazwa = p_nazwa THEN SELECT * INTO p_osoby(z_licznik) FROM osoby WHERE id = z_tmp.id; DBMS_OUTPUT.PUT_LINE(p_Osoby(z_Licznik).id '. ' p_osoby(z_licznik).nazwisko ' ' p_osoby(z_licznik).imie1 ' ' p_osoby(z_licznik).d_ur ' ' p_osoby(z_licznik).plec); z_licznik := z_licznik + 1; END IF; END p_osoby; / -- testowanie dzialania ostatniej procedury z_zatrudnieni p_osoby.t_osobyzatrudnione; z_nazwa wydzialy.nazwa%type := 'matematyka'; p_osoby.wypiszzatrudnionych(z_nazwa,z_zatrudnieni);
Zajęcia 11 wyzwalacze 1. Zaprojektować wyzwalacz DML na poziomie wiersza ktory bedzie umozliwial wypelnienie pola id w tabeli osoby nastepna liczba z uprzednio utworzonej sekwencji. -- tworzenie sekwencji CREATE SEQUENCE licz_id_os START WITH 33 INCREMENT BY 1; -- wartość początkowa zależy od maksymalnego osoby.id obecnie -- wyzwalacz CREATE OR REPLACE TRIGGER w_osobyidsekwencyjne BEFORE INSERT ON osoby FOR EACH ROW -- kursor niejawny, a nie UPDATE, bo zaszłoby zjawisko mutacji SELECT licz_id_os.nextval INTO :new.id FROM DUAL; END w_osobyidsekwencyjne; / -- test INSERT INTO osoby VALUES(-3,'Pazdzioch','Marian',NULL,SYSDATE,'M'); -- okazuje się, że wstawiła się następna wartość sekwencji 2. Mamy zaprojektowac wyzwalacz DML, ktory w sposob ciagly bedzie aktualizowal tabele zatrudn_liczba (nazwa, liczba_os) ktora zawiera informacje o liczbie akutlanie zatrudnionych osob na poszukiwanych wydzialach. DROP TABLE zatrudn_liczba; CREATE TABLE zatrudn_liczba ( nazwa_wydzialu VARCHAR2(20), liczba_os NUMBER); CREATE OR REPLACE TRIGGER w_aktualizujzatr AFTER INSERT OR UPDATE OR DELETE ON zatrudnienia CURSOR c_zatr IS SELECT w.nazwa, COUNT (o.id) liczba_os FROM wydzialy w, osoby o, zatrudnienia z WHERE w.id = z.id_wydz AND o.id = z.id_os AND z.do IS NULL GROUP BY w.nazwa; DELETE zatrudn_liczba; FOR z_tmp IN c_zatr LOOP INSERT INTO zatrudn_liczba VALUES (z_tmp.nazwa,z_tmp.liczba_os);
3. Mamy wykorzystac wyzwalacz DML do zapisu wszystkich dzialan wykonywanych na tabeli zatrudnienia. W tym celu nalezy utworzyc tabele zapis, w ktorej beda gromadzone zapisy o podjetych dzialaniach na tabeli zatrudnienia wyzwalacz powienien posiadac takze obsluge bledu w tkorej powinien byc wyswietlany kod popelnionego bledu. zapis (id number <== sekwencja, akcja varchar2(12) <= nazwa operacji DML data date <= data operacji uzyt varchar2(25) <= nazwa uzytkownika CREATE SEQUENCE l_zapisid START WITH 0 INCREMENT BY 1; CREATE TABLE zapis ( id NUMBER PRIMARY KEY, akcja VARCHAR2(12) NOT NULL, data DATE NOT NULL, uzytkownik VARCHAR2(25) NOT NULL); CREATE OR REPLACE TRIGGER w_zapisdzialan AFTER INSERT OR UPDATE OR DELETE ON zatrudnienia FOR EACH ROW z_kodbledu NUMBER; z_rodzajoperacji VARCHAR2(20); IF INSERTING THEN z_rodzajoperacji := 'wstawianie'; ELSIF UPDATING THEN z_rodzajoperacji := 'aktualizowanie'; ELSE z_rodzajoperacji := 'usuwanie'; END IF; INSERT INTO zapis VALUES ( l_zapisid.nextval, z_rodzajoperacji, SYSDATE, USER); EXCEPTION WHEN OTHERS THEN z_kodbledu := SQLCODE; DBMS_OUTPUT.PUT_LINE('Kod bledu: ' z_kodbledu);
4. Mamy zaprojektowac wyzwalacz DML na poziomie wiersza, ktory bedzie uniemozliwial wprowadzenie daty zwolnienia osoby wczesniejszej niz niz data zwolnienia. Ma wyswietlac odpowiedni komunikat. CREATE OR REPLACE TRIGGER w_blokujzledatyzatrudnienia BEFORE INSERT ON zatrudnienia FOR EACH ROW IF :new.do IS NOT NULL AND :new.od > :new.do THEN RAISE_APPLICATION_ERROR(-20000,'Data zatrudnienia pozniejsza, niz data zwolnienia'); END IF;
Ćwiczenia 12 wyzwalacze, ciąg dalszy 1. Zaprojektowac trigger DML, ktory bedzie blokowal wprowadzenie operacja INSERT lub UPDATE nazwy wydzialu, ktora juz uprzednio zostala pisana do tabeli wydzialy. CREATE OR REPLACE PACKAGE p_blokowanywydz AS z_nazwa wydzialy.nazwa%type; END p_blokowanywydz; / CREATE OR REPLACE TRIGGER w_blokujwydz1 BEFORE INSERT OR UPDATE ON wydzialy FOR EACH ROW p_blokowanywydz.z_nazwa := :new.nazwa; / CREATE OR REPLACE TRIGGER w_blokujwydz2 AFTER INSERT OR UPDATE ON wydzialy z_nazwa wydzialy.nazwa%type; SELECT nazwa INTO z_nazwa FROM wydzialy WHERE nazwa = p_blokowanywydz.z_nazwa; EXCEPTION WHEN TOO_MANY_ROWS THEN RAISE_APPLICATION_ERROR(-20000,'Wprowadzana nazwa wydzialu juz istnieje!'); / -- sprawdzenie INSERT INTO wydzialy VALUES (4,'prawo',2);