Dany jest następujący logiczny schemat bazy danych Systemy baz danych laboratorium Projekt zaliczeniowy Każda faktura jest identyfikowana przez unikalny identyfikator (f_id_faktury). Na fakturze umieszczone są data wystawienia (f_data_wystawienia) oraz data płatności (f_data_platnosci), przy czym data płatności musi być późniejsza niż data wystawienia faktury. Z każdą fakturą związana jest flaga oznaczająca, czy dana faktura została już zapłacona (f_czy_zaplacona, pole przyjmuje wartości T i N ). Faktura składa się z pozycji, każda pozycja posiada sztuczny identyfikator (p_id_pozycji) oraz atrybut p_f_id_faktury będący kluczem obcym wskazującym na fakturę, której dotyczy dana pozycja faktury. Każda pozycja faktury posiada liczbę porządkową (p_lp), poszczególne pozycje w ramach faktury są numerowane począwszy od liczby 1. Pozycja faktury posiada ponadto swoją nazwę (p_nazwa), ilość zakupionego towaru (p_ilosc), oraz cenę jednostkową (p_cena_jednostkowa) za towar (cena netto za sztukę, kilogram, litr, itp.) Towary opisywane przez pozycje faktur mogą podlegać trzem różnym stawkom podatku VAT (p_stawka_vat, pole przyjmuje wartości: 0%, 7%, 15% i 22%). Wykorzystaj skrypt faktury.sql do utworzenia powyższych tabel i załadowania przykładowych danych. Następnie, przygotuj odpowiedzi do zadań. W tabelce poniżej przedstawiono zasady oceniania. Zgodnie z sylabusem każdy student ma prawo do jednej nieobecności nieusprawiedliwionej, a każda następna nieusprawiedliwiona nieobecność skutkuje obniżeniem oceny końcowej o pół stopnia. Pamiętaj, aby uwzględnić koszt swoich nieusprawiedliwionych nieobecności przygotowując projekt zaliczeniowy. Ocena poprawnie wykonane zadania dostateczny 1, 2, 3, 4 dostateczny plus 1, 2, 3, 4, 5, 6 dobry 1, 2, 3, 4, 5, 6, 7, 8 dobry plus 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 bardzo dobry 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 Wszystkie odpowiedzi umieść w pliku tekstowym o nazwie INFxxx.SQL (gdzie xxx to Twój numer indeksu). Poprawne rozwiązania przedstawiono przy każdym zadaniu. Plik z rozwiązaniami prześlij na adres Mikolaj.Morzy@put.poznan.pl. Termin przesyłania rozwiązań upływa w niedzielę, 22 stycznia 2012, o godzinie 23:59. Żadne wpisy nie będą antydatowane, projekty przesłane po terminie będę oceniał tylko i wyłącznie po przedstawieniu przedłużenia sesji. Wszystkie rozwiązania zostaną przeanalizowane przez oprogramowanie do wykrywania podobieństw w kodzie źródłowym. Jeśli znajdę jakikolwiek plagiat, to obie prace otrzymują automatycznie ocenę niedostateczną i nie ma dla mnie znaczenia kwestia oryginalnego autorstwa rozwiązania. Uwaga: wszystkie obiekty (tabele FAKTURY i POZYCJE oraz wszystkie obiekty wykonane w ramach projektu) muszą się znajdować na koncie studenta na serwerze DCS-MM. Jest to warunek konieczny sprawdzenia projektu.
1. Napisz polecenie języka SQL które stworzy perspektywę v_faktury prezentującą, dla każdej faktury, identyfikator faktury, oraz liczbę pozycji faktury i sumaryczne kwoty faktury netto i brutto (dla każdej pozycji należy pomnożyć ilość zakupionego towaru przez cenę jednostkową, dla kolumny brutto należy dodatkowo uwzględnić podatek VAT). Nie zapomnij o zaokrągleniu wyniku do dwóch miejsc dziesiętnych. SELECT * FROM v_faktury; F_ID_FAKTURY LICZBA_POZYCJI SUMA_NETTO SUMA_BRUTTO ------------ -------------- ---------- ----------- 30 4 20.93 24.97 20 4 39.19 45.2 40 4 38.87 45.81 50 5 30.93 36.57 10 6 57.49 63.34 2 Napisz anonimowy blok PLSQL zawierający kursor sparametryzowany. Parametrem kursora jest nazwa produktu. Kursor przebiega przez wszystkie faktury zawierające podany produkt i sprawdza, czy dana faktura została już zapłacona. Jeśli nie została zapłacona, to program drukuje identyfikator faktury i liczbę dni spóźnienia z płatnością. Jeśli faktura została zapłacona, to program drukuje identyfikator faktury i datę płatności. W bloku PLSQL otwórz kursor, przekaż nazwę produktu przez zmienną środowiskową i wyświetl zawartość kursora za pomocą procedury DBMS_OUTPUT.PUT_LINE() SET SERVEROUTPUT ON VARIABLE produkt VARCHAR2(100) :produkt := 'majonez dekoracyjny'; PLSQL procedure successfully completed. DECLARE...ciało anonimowego bloku PLSQL Faktura 30 została zapłacona w dniu 07-MAJ-11 PLSQL procedure successfully completed. :produkt := 'jajka delikatesowe 10szt.'; PLSQL procedure successfully completed. DECLARE...ciało anonimowego bloku PLSQL Faktura 40 jest niezapłacona od 179 dni PLSQL procedure successfully completed.
3 Dodaj do powyższego bloku PLSQL procedurę obsługi błędu polegającego na podaniu nazwy nieistniejącego produktu. SET SERVEROUTPUT ON VARIABLE produkt VARCHAR2(100) :produkt := 'kaszanka delikatesowa'; PLSQL procedure successfully completed. DECLARE...ciało anonimowego bloku PLSQL Produkt kaszanka delikatesowa nie występuje na żadnej fakturze PLSQL procedure successfully completed. 4 Napisz samodzielną funkcję KWOTA_FAKTURY(f_id_faktury NUMBER) która przyjmuje, jako parametr, identyfikator faktury, i zwraca sumaryczną kwotę na fakturze (iloczyn ilości i ceny jednostkowej netto wszystkich pozycji powiększonych o kwoty należnego podatku). Pamiętaj, aby poprawnie zaokrąglić wynik. SELECT f_id_faktury, KWOTA_FAKTURY(f_id_faktury) AS kwota_faktury FROM faktury; F_ID_FAKTURY KWOTA_FAKTURY ------------ ------------- 10 63.34 20 45.2 30 24.97 40 45.81 50 36.57
5 Dodaj do tabeli FAKTURY atrybut f_kara_za_zwloke. Następnie, napisz procedurę o nazwie UAKTUALNIJ_FAKTURY(), która dla wszystkich niezapłaconych faktur przeliczy i uaktualni wartość atrybutu f_kara_za_zwloke. Przyjmij, że za każdy dzień spóźnienia w płatności faktury naliczana jest kara w wysokości 0.1% (jeden promil, jedna dziesiąta procenta) kwoty netto faktury. Pamiętaj, że kara za zwłokę naliczana jest z dokładnością do groszy (a nie np. setnych części grosza). Wylicz stan kar na dzień 1 stycznia 2012 roku. F_ID_FAKTURY F_DATA_WY F_DATA_PL F F_KARA_ZA_ZWLOKE ------------ --------- --------- - ---------------- 10 13-JAN-11 27-JAN-11 T 20 18-MAR-11 25-MAR-11 N 30 23-APR-11 07-MAY-11 T 40 09-JUL-11 19-JUL-11 N 50 15-NOV-11 29-NOV-11 T uaktualnij_faktury; PLSQL procedure successfully completed. F_ID_FAKTURY F_DATA_WY F_DATA_PL F F_KARA_ZA_ZWLOKE ------------ --------- --------- - ---------------- 10 13-JAN-12 27-JAN-12 T 20 18-MAR-11 25-MAR-11 N 11.05 30 23-APR-11 07-MAY-11 T 40 09-JUL-11 19-JUL-11 N 6.45 50 15-NOV-11 29-NOV-11 T 6 Dodaj do procedury UAKTUALNIJ_FAKTURY() obsługę błędu polegającego na tym, że faktura nie posiada żadnych pozycji faktury. W takim przypadku wartością atrybutu f_kara_za_zwloke jest wartość 0. Dodaj nową niezapłaconą fakturę bez pozycji faktury i sprawdź działanie procedury. INSERT INTO faktury (f_id_faktury, f_czy_zaplacona) VALUES (60, 'N'); uaktualnij_faktury; PLSQL procedure successfully completed. F_ID_FAKTURY F_DATA_WY F_DATA_PL F F_KARA_ZA_ZWLOKE ------------ --------- --------- - ---------------- 60 15-JAN-12 N 0 10 13-JAN-11 27-JAN-11 T 20 18-MAR-11 25-MAR-11 N 11.05 30 23-APR-11 07-MAY-11 T 40 09-JUL-11 19-JUL-11 N 6.45 50 15-NOV-11 29-NOV-11 T
7 Stwórz wyzwalacz (lub trzy oddzielne wyzwalacze) który będzie odnotowywał każdą modyfikację tabeli FAKTURA. Zbuduj tablę HISTORIA(id TIMESTAMP, operacja VARCHAR(30)) i umieszczaj tam znacznik czasowy (SYSTIMESTAMP) każdej operacji INSERT, UPDATE, DELETE kierowanej do tabeli FAKTURA oraz nazwę wykonanej operacji. Sprawdź działanie wyzwalacza. INSERT INTO faktury(f_id_faktury, f_data_wystawienia, f_czy_zaplacona) VALUES (70, SYSDATE, 'N'); UPDATE faktury SET f_data_platnosci = f_data_wystawienia + 14 WHERE f_id_faktury = 70; 1 row updated. DELETE FROM faktury WHERE f_id_faktury = 70; 1 row deleted. SQL> SELECT * FROM historia; ID OPERACJA ------------------------------ ----------------------------- 15-JAN-12 01.39.01.461704 AM wstawienie faktury 15-JAN-12 01.39.12.058893 AM modyfikacja faktury 15-JAN-12 01.39.19.286247 AM usunięcie faktury 8 Utwórz sekwencję faktury_seq. Napisz wyzwalacz umożliwiający dodawanie nowej faktury bez podania wartości klucza podstawowego (wartość klucza powinna być wczytana z sekwencji przez wyzwalacz, ale tylko w przypadku, gdy użytkownik nie podał wartości klucza podstawowego!). INSERT INTO faktury(f_data_wystawienia, f_data_platnosci) VALUES (SYSDATE, SYSDATE + 14); INSERT INTO faktury(f_id_faktury, f_data_wystawienia, f_data_platnosci) VALUES (70, SYSDATE, SYSDATE + 14); F_ID_FAKTURY F_DATA_WY F_DATA_PL F F_KARA_ZA_ZWLOKE ------------ --------- --------- - ---------------- 60 15-JAN-12 N 0 70 15-JAN-12 29-JAN-12 80 15-JAN-12 29-JAN-12 10 13-JAN-11 27-JAN-11 T 20 18-MAR-11 25-MAR-11 N 11.05 30 23-APR-11 07-MAY-11 T 40 09-JUL-11 19-JUL-11 N 6.45 50 15-NOV-11 29-NOV-11 T 8 rows selected.
9 Dodaj do tabeli FAKTURY atrybut f_liczba_pozycji. Zainicjalizuj wartości tego atrybutu we wszystkich krotkach relacji FAKTURY. Stwórz wyzwalacz, który będzie pielęgnował wartość atrybutu f_liczba_pozycji. Po każdej modyfikacji tabeli POZYCJE (dodanie lub usunięcie pozycji) odpowiednio zmodyfikuj atrybut. Uwaga: nie wolno przenosić pozycji faktury między fakturami, wyzwalacz musi zapobiec takiej operacji! SELECT * FROM faktury WHERE f_id_faktury = 80; F_ID_FAKTURY F_DATA_WY F_DATA_PL F F_KARA_ZA_ZWLOKE F_LICZBA_POZYCJI ------------ --------- --------- - ---------------- ---------------- 80 15-JAN-12 29-JAN-12 0 INSERT INTO pozycje (p_id_pozycji,p_f_id_faktury,p_lp,p_nazwa) VALUES (123,80,1,'sałatka śledziowa'); SELECT * FROM faktury WHERE f_id_faktury = 80; F_ID_FAKTURY F_DATA_WY F_DATA_PL F F_KARA_ZA_ZWLOKE F_LICZBA_POZYCJI ------------ --------- --------- - ---------------- ---------------- 80 15-JAN-12 29-JAN-12 1 UPDATE pozycje SET p_f_id_faktury = 40 WHERE p_id_pozycji = 123; UPDATE pozycje SET p_f_id_faktury = 40 WHERE p_id_pozycji = 123 * ERROR at line 1: ORA-20001: Nie wolno przenieść pozycji do innej faktury ORA-06512: at "MIKOLAJ.TRIG_LICZBA_POZYCJI", line 7 ORA-04088: error during execution of trigger 'MIKOLAJ.TRIG_LICZBA_POZYCJI' DELETE FROM pozycje WHERE p_id_pozycji = 123; 1 row deleted. SQL> SELECT * FROM faktury WHERE f_id_faktury = 80; F_ID_FAKTURY F_DATA_WY F_DATA_PL F F_KARA_ZA_ZWLOKE F_LICZBA_POZYCJI ------------ --------- --------- - ---------------- ---------------- 80 15-JAN-12 29-JAN-12 0
10 Napisz pakiet FINANSE składający się z procedury KOPIA_FAKTURY(p_id NUMBER), która dla faktury o podanym identyfikatorze wstawia do tabel FAKTURY i POZYCJE kopię danej faktury (wraz z pozycjami), z identyfikatorami zwiększonymi o 1000 w stosunku do oryginalnych identyfikatorów (faktury i pozycji) funkcji LICZBA_FAKTUR(p_miesiac NUMBER, p_rok NUMBER), która zwraca liczbę faktur wystawionych w danym miesiącu danego roku. Przetestuj działanie stworzonego przez siebie pakietu. finanse.kopia_faktury(10); F_ID_FAKTURY F_DATA_W F_DATA_P F F_LACZNA_KWOTA F_LICZBA_POZYCJI ------------ -------- -------- - -------------- ---------------- 10 100113 100127 T 63,35 6 20 100318 100325 N 4 30 100423 100507 T 24,98 4 40 100709 100719 N 4 50 101115 101129 T 36,58 5 60 110110 110112 T -1 0 90 101231 0 80 110115 110122 0 1010 100113 100127 T SELECT * FROM pozycje WHERE p_f_id_faktury = 1010; P_ID_POZYCJI P_F_ID_FAKTURY P_LP P_NAZWA P_ILOSC P_CENA_JEDNOSTKOWA P_STAWKA_VAT ------------ -------------- ---------- ----------------------------------- ---------- ------------------------------- 1101 1010 2 piwo Lech Pils 1 2,99,22 1102 1010 3 mleko Łaciate 2% UHT 1 1,99,15 1100 1010 1 chleb baltonowski 2 2,54,22 1103 1010 4 wino Chateau de Lussan Cru Artisan 1 39,99,07 1104 1010 5 czipsy X-Lays papryka 1 4,45,22 1121 1010 6 sok jabłkowy Hortex 1l 1 2,99 0 SELECT finanse.liczba_faktur(1,2011) AS liczba_faktur_styczen_2011 FROM dual; LICZBA_FAKTUR_STYCZEN_2011 -------------------------- 2
11 Zmodyfikuj pakiet FINANSE. Procedura KOPIA_FAKTURY powinna sprawdzać, czy w schemacie użytkownika występują tabele FAKTURY_KOPIE i POZYCJE_KOPIE (o strukturze identycznej jak struktura oryginalnej tabeli, odpowiednio). Jeśli tabele istnieją, to procedura powinna tam tworzyć kopie faktur i pozycji. Jeśli tabele nie istnieją, to procedura powinna je najpierw stworzyć (dynamiczny SQL). Podczas wstawiania kopii faktur i pozycji należy zachować oryginalne wartości identyfikatorów (a nie zwiększone o 1000, jak w poprzednim ćwiczeniu). SELECT * FROM faktury_kopie; SELECT * FROM faktury_kopie * BŁĄD w linii 1: ORA-00942: tabela lub perspektywa nie istnieje SELECT * FROM pozycje_kopie; SELECT * FROM pozycje_kopie * BŁĄD w linii 1: ORA-00942: tabela lub perspektywa nie istnieje finanse.kopia_faktury(20); Procedura PLSQL została zakończona pomyślnie. SELECT * FROM faktury_kopie; F_ID_FAKTURY F_DATA_W F_DATA_P F F_LACZNA_KWOTA F_LICZBA_POZYCJI ------------ -------- -------- - -------------- ---------------- 20 100318 100325 N SELECT * FROM pozycje_kopie; P_ID_POZYCJI P_F_ID_FAKTURY P_LP P_NAZWA P_ILOSC P_CENA_JEDNOSTKOWA P_STAWKA_VAT ------------ -------------- ---------- ----------------------------------- ---------- ------------------------------- 105 20 1 ogórek zielony 1,5 4,49,22 106 20 2 sałatka porowa,3 5,22 107 20 3 makrela wędzona 3,5 7,99,15 108 20 4 sok jabłkowy Hortex 1l 1 2,99 0 12 Dodaj do pakietu FINANSE zmienną globalną przechowującą liczbę kopii faktur wykonanych w danej sesji. Dodaj do pakietu funkcję LICZBA_KOPII zwracającą zawartość globalnej zmiennej pakietowej. Przetestuj zachowanie się zmiennej podczas tworzenia kopii kilku faktur. SELECT finanse.liczba_kopii FROM dual; LICZBA_KOPII ------------ 0 finanse.kopia_faktury(30); finanse.kopia_faktury(40); Procedura PLSQL została zakończona pomyślnie. SELECT finanse.liczba_kopii FROM dual; LICZBA_KOPII ------------ 2