-- IDS - project 4th part -- Roman Nečas - xnecasr00 -- Kristián Pribila - xpribik00 -- 03.05.2025 -- Drop existing tables (if they exist) DROP TABLE Recenzia CASCADE CONSTRAINTS; DROP TABLE Vypozicka CASCADE CONSTRAINTS; DROP TABLE Rezervacia CASCADE CONSTRAINTS; DROP TABLE Exemplar CASCADE CONSTRAINTS; DROP TABLE Kniha CASCADE CONSTRAINTS; DROP TABLE Casopis CASCADE CONSTRAINTS; DROP TABLE Titul CASCADE CONSTRAINTS; DROP TABLE Citatel CASCADE CONSTRAINTS; DROP TABLE Zamestnanec CASCADE CONSTRAINTS; DROP TABLE Pouzivatel CASCADE CONSTRAINTS; -- Drop sequences (if they exist) DROP SEQUENCE pouzivatel_seq; DROP SEQUENCE titul_seq; DROP SEQUENCE exemplar_seq; DROP SEQUENCE rezervacia_seq; DROP SEQUENCE vypozicka_seq; DROP SEQUENCE recenzia_seq; -- Drop procedures (if they exist) BEGIN EXECUTE IMMEDIATE 'DROP PROCEDURE spracuj_rezervaciu'; EXCEPTION WHEN OTHERS THEN NULL; END; / BEGIN EXECUTE IMMEDIATE 'DROP PROCEDURE generuj_report_oneskorene'; EXCEPTION WHEN OTHERS THEN NULL; END; / BEGIN EXECUTE IMMEDIATE 'DROP PROCEDURE aktualizuj_oneskorene_vypozicky'; EXCEPTION WHEN OTHERS THEN NULL; END; / -- Create sequences for primary keys CREATE SEQUENCE pouzivatel_seq START WITH 1 INCREMENT BY 1; CREATE SEQUENCE titul_seq START WITH 1 INCREMENT BY 1; CREATE SEQUENCE exemplar_seq START WITH 1 INCREMENT BY 1; CREATE SEQUENCE rezervacia_seq START WITH 1 INCREMENT BY 1; CREATE SEQUENCE vypozicka_seq START WITH 1 INCREMENT BY 1; CREATE SEQUENCE recenzia_seq START WITH 1 INCREMENT BY 1; -- Create tables -- Implementation of generalization/specialization: -- We are using the approach where parent tables (Pouzivatel and Titul) contain common attributes, -- and child tables (Citatel, Zamestnanec, Kniha, Casopis) contain specific attributes. -- Child tables have primary keys that are also foreign keys referencing parent tables. -- This allows us to maintain the integrity of the inheritance relationship while -- having specific attributes for each specialization. -- Parent table for Users CREATE TABLE Pouzivatel ( ID_pouzivatela NUMBER PRIMARY KEY, Meno VARCHAR2(50) NOT NULL, Priezvisko VARCHAR2(50) NOT NULL, Email VARCHAR2(100) NOT NULL UNIQUE, Telefon VARCHAR2(20), Adresa VARCHAR2(200), Datum_registracie DATE DEFAULT SYSDATE NOT NULL ); -- Child table for Readers CREATE TABLE Citatel ( ID_pouzivatela NUMBER PRIMARY KEY, Cislo_preukazu VARCHAR2(20) NOT NULL UNIQUE, Platnost_do DATE NOT NULL, Status VARCHAR2(20) DEFAULT 'Aktívny' CHECK (Status IN ('Aktívny', 'Neaktívny', 'Pozastavený')), Pocet_vypoziciek NUMBER DEFAULT 0, CONSTRAINT fk_citatel_pouzivatel FOREIGN KEY (ID_pouzivatela) REFERENCES Pouzivatel(ID_pouzivatela) ON DELETE CASCADE ); -- Child table for Employees CREATE TABLE Zamestnanec ( ID_pouzivatela NUMBER PRIMARY KEY, Osobne_cislo VARCHAR2(20) NOT NULL UNIQUE, Datum_nastupu DATE NOT NULL, Oddelenie VARCHAR2(50) NOT NULL, Titul VARCHAR2(20), CONSTRAINT fk_zamestnanec_pouzivatel FOREIGN KEY (ID_pouzivatela) REFERENCES Pouzivatel(ID_pouzivatela) ON DELETE CASCADE ); -- Parent table for Titles CREATE TABLE Titul ( ID_titulu NUMBER PRIMARY KEY, Nazov VARCHAR2(200) NOT NULL, Vydavatelstvo VARCHAR2(100), Rok_vydania NUMBER(4), Popis VARCHAR2(4000), Kategoria VARCHAR2(50), Pocet_exemplarov NUMBER DEFAULT 0 ); -- Child table for Books with ISBN validation CREATE TABLE Kniha ( ID_titulu NUMBER PRIMARY KEY, ISBN VARCHAR2(13) NOT NULL UNIQUE, Autor VARCHAR2(200) NOT NULL, Pocet_stran NUMBER, Zaner VARCHAR2(50), CONSTRAINT fk_kniha_titul FOREIGN KEY (ID_titulu) REFERENCES Titul(ID_titulu) ON DELETE CASCADE, -- ISBN must be exactly 13 digits CONSTRAINT chk_isbn CHECK (REGEXP_LIKE(ISBN, '^[0-9]{13}$')) ); -- Child table for Magazines with ISSN validation CREATE TABLE Casopis ( ID_titulu NUMBER PRIMARY KEY, ISSN VARCHAR2(9) NOT NULL UNIQUE, Rocnik NUMBER, Cislo NUMBER, Periodicita VARCHAR2(50), CONSTRAINT fk_casopis_titul FOREIGN KEY (ID_titulu) REFERENCES Titul(ID_titulu) ON DELETE CASCADE, -- ISSN must be in format XXXX-XXXX where X is a digit CONSTRAINT chk_issn CHECK (REGEXP_LIKE(ISSN, '^[0-9]{4}-[0-9]{4}$')) ); -- Table for Copies CREATE TABLE Exemplar ( ID_exemplaru NUMBER PRIMARY KEY, ID_titulu NUMBER NOT NULL, Datum_nadobudnutia DATE, Umiestnenie VARCHAR2(100), Signatura VARCHAR2(50), Status VARCHAR2(20) DEFAULT 'Dostupný' CHECK (Status IN ('Dostupný', 'Vypožičaný', 'Rezervovaný', 'Vyradený', 'Poškodený')), CONSTRAINT fk_exemplar_titul FOREIGN KEY (ID_titulu) REFERENCES Titul(ID_titulu) ON DELETE CASCADE ); -- Table for Reservations CREATE TABLE Rezervacia ( ID_rezervacie NUMBER PRIMARY KEY, ID_pouzivatela NUMBER NOT NULL, ID_titulu NUMBER NOT NULL, Datum_rezervacie DATE DEFAULT SYSDATE NOT NULL, Datum_expiracie DATE, Stav VARCHAR2(20) DEFAULT 'Aktívna' CHECK (Stav IN ('Aktívna', 'Vybavená', 'Zrušená')), CONSTRAINT fk_rezervacia_pouzivatel FOREIGN KEY (ID_pouzivatela) REFERENCES Pouzivatel(ID_pouzivatela), CONSTRAINT fk_rezervacia_titul FOREIGN KEY (ID_titulu) REFERENCES Titul(ID_titulu) ); -- Table for Loans CREATE TABLE Vypozicka ( ID_vypozicky NUMBER PRIMARY KEY, ID_pouzivatela NUMBER NOT NULL, ID_exemplaru NUMBER NOT NULL, Datum_vypozicania DATE DEFAULT SYSDATE NOT NULL, Planovany_datum_vratenia DATE NOT NULL, Skutocny_datum_vratenia DATE, Stav VARCHAR2(20) DEFAULT 'Aktívna' CHECK (Stav IN ('Aktívna', 'Vrátená', 'Oneskorená')), CONSTRAINT fk_vypozicka_pouzivatel FOREIGN KEY (ID_pouzivatela) REFERENCES Pouzivatel(ID_pouzivatela), CONSTRAINT fk_vypozicka_exemplar FOREIGN KEY (ID_exemplaru) REFERENCES Exemplar(ID_exemplaru) ); -- Table for Reviews CREATE TABLE Recenzia ( ID_recenzie NUMBER PRIMARY KEY, ID_pouzivatela NUMBER NOT NULL, ID_titulu NUMBER NOT NULL, Hodnotenie NUMBER(1) CHECK (Hodnotenie BETWEEN 1 AND 5), Sprava VARCHAR2(2000), Datum DATE DEFAULT SYSDATE NOT NULL, CONSTRAINT fk_recenzia_pouzivatel FOREIGN KEY (ID_pouzivatela) REFERENCES Pouzivatel(ID_pouzivatela), CONSTRAINT fk_recenzia_titul FOREIGN KEY (ID_titulu) REFERENCES Titul(ID_titulu) ); -- Create triggers for auto-incrementing primary keys from sequences CREATE OR REPLACE TRIGGER pouzivatel_bir BEFORE INSERT ON Pouzivatel FOR EACH ROW BEGIN IF :NEW.ID_pouzivatela IS NULL THEN SELECT pouzivatel_seq.NEXTVAL INTO :NEW.ID_pouzivatela FROM dual; END IF; END; / CREATE OR REPLACE TRIGGER titul_bir BEFORE INSERT ON Titul FOR EACH ROW BEGIN IF :NEW.ID_titulu IS NULL THEN SELECT titul_seq.NEXTVAL INTO :NEW.ID_titulu FROM dual; END IF; END; / CREATE OR REPLACE TRIGGER exemplar_bir BEFORE INSERT ON Exemplar FOR EACH ROW BEGIN IF :NEW.ID_exemplaru IS NULL THEN SELECT exemplar_seq.NEXTVAL INTO :NEW.ID_exemplaru FROM dual; END IF; END; / CREATE OR REPLACE TRIGGER rezervacia_bir BEFORE INSERT ON Rezervacia FOR EACH ROW BEGIN IF :NEW.ID_rezervacie IS NULL THEN SELECT rezervacia_seq.NEXTVAL INTO :NEW.ID_rezervacie FROM dual; END IF; END; / CREATE OR REPLACE TRIGGER vypozicka_bir BEFORE INSERT ON Vypozicka FOR EACH ROW BEGIN IF :NEW.ID_vypozicky IS NULL THEN SELECT vypozicka_seq.NEXTVAL INTO :NEW.ID_vypozicky FROM dual; END IF; END; / CREATE OR REPLACE TRIGGER recenzia_bir BEFORE INSERT ON Recenzia FOR EACH ROW BEGIN IF :NEW.ID_recenzie IS NULL THEN SELECT recenzia_seq.NEXTVAL INTO :NEW.ID_recenzie FROM dual; END IF; END; / -- Insert sample data -- Insert Users INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Ján', 'Novák', 'jan.novak@example.com', '+421901234567', 'Bratislavská 123, Bratislava', TO_DATE('2022-01-15', 'YYYY-MM-DD')); INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Anna', 'Králová', 'anna.kralova@example.com', '+421902345678', 'Košická 456, Košice', TO_DATE('2022-02-20', 'YYYY-MM-DD')); INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Peter', 'Malý', 'peter.maly@example.com', '+421903456789', 'Hlavná 789, Žilina', TO_DATE('2022-03-10', 'YYYY-MM-DD')); INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Eva', 'Veľká', 'eva.velka@example.com', '+421904567890', 'Námestie SNP 10, Banská Bystrica', TO_DATE('2022-04-05', 'YYYY-MM-DD')); INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Milan', 'Kovář', 'milan.kovar@example.com', '+421905678901', 'Moyzesova 321, Nitra', TO_DATE('2022-05-12', 'YYYY-MM-DD')); -- Insert Readers INSERT INTO Citatel (ID_pouzivatela, Cislo_preukazu, Platnost_do, Status, Pocet_vypoziciek) VALUES (1, 'CIT-001', TO_DATE('2025-01-15', 'YYYY-MM-DD'), 'Aktívny', 0); INSERT INTO Citatel (ID_pouzivatela, Cislo_preukazu, Platnost_do, Status, Pocet_vypoziciek) VALUES (2, 'CIT-002', TO_DATE('2025-02-20', 'YYYY-MM-DD'), 'Aktívny', 0); INSERT INTO Citatel (ID_pouzivatela, Cislo_preukazu, Platnost_do, Status, Pocet_vypoziciek) VALUES (3, 'CIT-003', TO_DATE('2025-03-10', 'YYYY-MM-DD'), 'Pozastavený', 0); -- Insert Employees INSERT INTO Zamestnanec (ID_pouzivatela, Osobne_cislo, Datum_nastupu, Oddelenie, Titul) VALUES (4, 'ZAM-001', TO_DATE('2020-04-05', 'YYYY-MM-DD'), 'Katalogizácia', 'Mgr.'); INSERT INTO Zamestnanec (ID_pouzivatela, Osobne_cislo, Datum_nastupu, Oddelenie, Titul) VALUES (5, 'ZAM-002', TO_DATE('2021-05-12', 'YYYY-MM-DD'), 'Výpožičky', 'Ing.'); -- Insert Titles INSERT INTO Titul (Nazov, Vydavatelstvo, Rok_vydania, Popis, Kategoria, Pocet_exemplarov) VALUES ('Pán Prsteňov: Spoločenstvo prsteňa', 'Slovart', 2001, 'Prvá časť fantasy trilógie od J.R.R. Tolkiena', 'Fantasy', 2); INSERT INTO Titul (Nazov, Vydavatelstvo, Rok_vydania, Popis, Kategoria, Pocet_exemplarov) VALUES ('Harry Potter a Kameň mudrcov', 'Ikar', 2000, 'Prvá kniha zo série o mladom čarodejníkovi', 'Fantasy', 1); INSERT INTO Titul (Nazov, Vydavatelstvo, Rok_vydania, Popis, Kategoria, Pocet_exemplarov) VALUES ('National Geographic', 'National Geographic Society', 2023, 'Populárno-náučný časopis', 'Veda', 1); INSERT INTO Titul (Nazov, Vydavatelstvo, Rok_vydania, Popis, Kategoria, Pocet_exemplarov) VALUES ('Forbes', 'Forbes Media', 2023, 'Ekonomický časopis', 'Biznis', 1); INSERT INTO Titul (Nazov, Vydavatelstvo, Rok_vydania, Popis, Kategoria, Pocet_exemplarov) VALUES ('1984', 'Slovart', 2015, 'Dystopický román od George Orwella', 'Sci-fi', 1); -- Insert Books INSERT INTO Kniha (ID_titulu, ISBN, Autor, Pocet_stran, Zaner) VALUES (1, '9788055603261', 'J.R.R. Tolkien', 432, 'Fantasy'); INSERT INTO Kniha (ID_titulu, ISBN, Autor, Pocet_stran, Zaner) VALUES (2, '9788055159423', 'J.K. Rowling', 320, 'Fantasy'); INSERT INTO Kniha (ID_titulu, ISBN, Autor, Pocet_stran, Zaner) VALUES (5, '9788055138887', 'George Orwell', 288, 'Sci-fi'); -- Insert Magazines INSERT INTO Casopis (ID_titulu, ISSN, Rocnik, Cislo, Periodicita) VALUES (3, '0027-9358', 2023, 3, 'Mesačník'); INSERT INTO Casopis (ID_titulu, ISSN, Rocnik, Cislo, Periodicita) VALUES (4, '0015-6914', 2023, 4, 'Mesačník'); -- Insert Copies INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (1, TO_DATE('2022-01-10', 'YYYY-MM-DD'), 'Regál A, Polica 1', 'F-TOL-001', 'Dostupný'); INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (1, TO_DATE('2022-01-10', 'YYYY-MM-DD'), 'Regál A, Polica 1', 'F-TOL-002', 'Dostupný'); INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (2, TO_DATE('2022-02-15', 'YYYY-MM-DD'), 'Regál A, Polica 2', 'F-ROW-001', 'Vypožičaný'); INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (3, TO_DATE('2023-03-01', 'YYYY-MM-DD'), 'Regál B, Polica 1', 'C-NAT-001', 'Dostupný'); INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (4, TO_DATE('2023-04-05', 'YYYY-MM-DD'), 'Regál B, Polica 2', 'C-FOR-001', 'Dostupný'); INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (5, TO_DATE('2022-06-10', 'YYYY-MM-DD'), 'Regál C, Polica 1', 'S-ORW-001', 'Vypožičaný'); -- Insert Reservations INSERT INTO Rezervacia (ID_pouzivatela, ID_titulu, Datum_rezervacie, Datum_expiracie, Stav) VALUES (1, 2, TO_DATE('2023-04-10', 'YYYY-MM-DD'), TO_DATE('2023-04-17', 'YYYY-MM-DD'), 'Aktívna'); INSERT INTO Rezervacia (ID_pouzivatela, ID_titulu, Datum_rezervacie, Datum_expiracie, Stav) VALUES (2, 5, TO_DATE('2023-04-12', 'YYYY-MM-DD'), TO_DATE('2023-04-19', 'YYYY-MM-DD'), 'Vybavená'); -- Insert Loans INSERT INTO Vypozicka (ID_pouzivatela, ID_exemplaru, Datum_vypozicania, Planovany_datum_vratenia, Skutocny_datum_vratenia, Stav) VALUES (1, 3, TO_DATE('2023-04-10', 'YYYY-MM-DD'), TO_DATE('2023-05-10', 'YYYY-MM-DD'), NULL, 'Aktívna'); INSERT INTO Vypozicka (ID_pouzivatela, ID_exemplaru, Datum_vypozicania, Planovany_datum_vratenia, Skutocny_datum_vratenia, Stav) VALUES (2, 6, TO_DATE('2023-04-12', 'YYYY-MM-DD'), TO_DATE('2023-05-12', 'YYYY-MM-DD'), TO_DATE('2023-04-30', 'YYYY-MM-DD'), 'Vrátená'); -- Insert Reviews INSERT INTO Recenzia (ID_pouzivatela, ID_titulu, Hodnotenie, Sprava, Datum) VALUES (1, 1, 5, 'Vynikajúca kniha, jedno z najlepších fantasy diel všetkých čias.', TO_DATE('2023-03-15', 'YYYY-MM-DD')); INSERT INTO Recenzia (ID_pouzivatela, ID_titulu, Hodnotenie, Sprava, Datum) VALUES (2, 2, 4, 'Skvelá kniha pre mladých čitateľov, fascinujúci magický svet.', TO_DATE('2023-03-20', 'YYYY-MM-DD')); INSERT INTO Recenzia (ID_pouzivatela, ID_titulu, Hodnotenie, Sprava, Datum) VALUES (3, 5, 5, 'Nadčasový dystopický román, stále aktuálny aj dnes.', TO_DATE('2023-03-25', 'YYYY-MM-DD')); -- Extra data for better testing INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Lucia', 'Horváthová', 'lucia.horvathova@example.com', '+421906789012', 'Dlhá 5, Trnava', TO_DATE('2022-06-01', 'YYYY-MM-DD')); INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Martin', 'Šimek', 'martin.simek@example.com', '+421907890123', 'Jarná 22, Prešov', TO_DATE('2022-07-10', 'YYYY-MM-DD')); INSERT INTO Pouzivatel (Meno, Priezvisko, Email, Telefon, Adresa, Datum_registracie) VALUES ('Barbora', 'Petrová', 'barbora.petrova@example.com', '+421908901234', 'Letná 7, Poprad', TO_DATE('2022-08-18', 'YYYY-MM-DD')); INSERT INTO Citatel (ID_pouzivatela, Cislo_preukazu, Platnost_do, Status, Pocet_vypoziciek) VALUES (6, 'CIT-004', TO_DATE('2025-06-01', 'YYYY-MM-DD'), 'Aktívny', 1); INSERT INTO Citatel (ID_pouzivatela, Cislo_preukazu, Platnost_do, Status, Pocet_vypoziciek) VALUES (7, 'CIT-005', TO_DATE('2025-07-10', 'YYYY-MM-DD'), 'Pozastavený', 0); INSERT INTO Zamestnanec (ID_pouzivatela, Osobne_cislo, Datum_nastupu, Oddelenie, Titul) VALUES (8, 'ZAM-003', TO_DATE('2021-08-18', 'YYYY-MM-DD'), 'Oddelenie vývoja databáz', 'PhDr.'); INSERT INTO Titul (Nazov, Vydavatelstvo, Rok_vydania, Popis, Kategoria, Pocet_exemplarov) VALUES ('Bratstvo čiernej dýky', 'Ikar', 2012, 'Moderný mestský fantasy román s upírskou tematikou', 'Fantasy', 1); INSERT INTO Titul (Nazov, Vydavatelstvo, Rok_vydania, Popis, Kategoria, Pocet_exemplarov) VALUES ('Sapiens: Stručná história ľudstva', 'Tatran', 2018, 'Fascinujúci pohľad na vývoj ľudskej civilizácie', 'História', 1); INSERT INTO Kniha (ID_titulu, ISBN, Autor, Pocet_stran, Zaner) VALUES (6, '9788055163223', 'J.R. Ward', 400, 'Fantasy'); INSERT INTO Kniha (ID_titulu, ISBN, Autor, Pocet_stran, Zaner) VALUES (7, '9788022209223', 'Yuval Noah Harari', 448, 'História'); INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (6, TO_DATE('2022-09-01', 'YYYY-MM-DD'), 'Regál D, Polica 1', 'F-WAR-001', 'Dostupný'); INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (7, TO_DATE('2022-10-01', 'YYYY-MM-DD'), 'Regál E, Polica 3', 'H-HAR-001', 'Vypožičaný'); INSERT INTO Rezervacia (ID_pouzivatela, ID_titulu, Datum_rezervacie, Datum_expiracie, Stav) VALUES (6, 6, TO_DATE('2023-06-05', 'YYYY-MM-DD'), TO_DATE('2023-06-12', 'YYYY-MM-DD'), 'Aktívna'); INSERT INTO Vypozicka (ID_pouzivatela, ID_exemplaru, Datum_vypozicania, Planovany_datum_vratenia, Skutocny_datum_vratenia, Stav) VALUES (6, 8, TO_DATE('2023-06-06', 'YYYY-MM-DD'), TO_DATE('2023-07-06', 'YYYY-MM-DD'), NULL, 'Aktívna'); INSERT INTO Recenzia (ID_pouzivatela, ID_titulu, Hodnotenie, Sprava, Datum) VALUES (6, 7, 5, 'Skvelo napísané a veľmi poučné. Harari píše pútavo.', TO_DATE('2023-06-10', 'YYYY-MM-DD')); --Selects -- Show information about employees SELECT p.meno || ' ' || p.priezvisko AS zamestnanec, p.ID_pouzivatela ,p.Telefon, p.Email, p.Adresa FROM Zamestnanec z JOIN Pouzivatel p ON z.ID_pouzivatela = p.ID_pouzivatela; -- Show information about reservations SELECT p.meno || ' ' || p.priezvisko AS citatel, r.ID_titulu, r.Datum_expiracie, r.Datum_rezervacie, r.Stav FROM Rezervacia r JOIN Pouzivatel p ON p.ID_pouzivatela = r.ID_pouzivatela; -- Show reviews SELECT p.meno || ' ' || p.priezvisko AS recenzent, t.Nazov, r.Hodnotenie, r.Sprava FROM Recenzia r JOIN Titul t ON r.ID_titulu = t.ID_titulu JOIN Pouzivatel p ON r.ID_pouzivatela = p.ID_pouzivatela ; -- Show available titles and their count SELECT t.Nazov, COUNT(e.ID_exemplaru) AS Pocet_dostupnych FROM Titul t JOIN Exemplar e ON t.ID_titulu = e.ID_titulu WHERE e.Status = 'Dostupný' GROUP BY t.Nazov; -- Show active borrowings SELECT p.Meno || ' ' || p.Priezvisko AS Citatel, t.Nazov AS Kniha, v.Datum_vypozicania, v.Planovany_datum_vratenia FROM Vypozicka v JOIN Pouzivatel p ON v.ID_pouzivatela = p.ID_pouzivatela JOIN Exemplar e ON v.ID_exemplaru = e.ID_exemplaru JOIN Titul t ON e.ID_titulu = t.ID_titulu WHERE v.Stav = 'Aktívna'; -- Show all names with their role SELECT p.ID_pouzivatela, p.Meno, p.Priezvisko, CASE WHEN EXISTS (SELECT 1 FROM Citatel c WHERE c.ID_pouzivatela = p.ID_pouzivatela) THEN 'Citatel' WHEN EXISTS (SELECT 1 FROM Zamestnanec z WHERE z.ID_pouzivatela = p.ID_pouzivatela) THEN 'Zamestnanec' ELSE 'Neznámy' END AS Typ_pouzivatela FROM Pouzivatel p; -- Show which books wasnt returned SELECT p.Meno || ' ' || p.Priezvisko AS citatel FROM Pouzivatel p WHERE EXISTS ( SELECT 1 FROM Vypozicka v WHERE v.ID_pouzivatela = p.ID_pouzivatela AND v.Skutocny_datum_vratenia IS NULL ); -- Show titles based on genre SELECT Nazov, Kategoria, Pocet_exemplarov FROM Titul WHERE Kategoria = 'Fantasy'; -- Show how many borrows and reservations reader made. SELECT p.Meno || ' ' || p.Priezvisko AS Citatel, c.ID_pouzivatela, v.Stav, COUNT(DISTINCT v.ID_vypozicky) AS pocet_vypoziciek, COUNT(DISTINCT r.ID_rezervacie) AS pocet_rezervaci FROM Citatel c JOIN Vypozicka v ON c.ID_pouzivatela = v.ID_pouzivatela JOIN Pouzivatel p ON c.ID_pouzivatela = p.ID_pouzivatela JOIN Rezervacia r ON p.ID_pouzivatela = r.ID_pouzivatela GROUP BY p.Meno, p.Priezvisko, c.ID_pouzivatela, v.Stav; -- Show only reviewed titles SELECT nazov FROM Titul WHERE id_titulu IN ( SELECT id_titulu FROM Recenzia ); COMMIT; -- PART 4 -- *** 1. DATABASE TRIGGERS *** -- Trigger 1: Update loan count CREATE OR REPLACE TRIGGER trigger_pocet_vypoziciek AFTER INSERT ON Vypozicka FOR EACH ROW BEGIN UPDATE Citatel SET Pocet_vypoziciek = Pocet_vypoziciek + 1 WHERE ID_pouzivatela = :NEW.ID_pouzivatela; END; / -- Trigger 2: Check if max limit for loans wasnt reached CREATE OR REPLACE TRIGGER trigger_maximalny_pocet_vypoziciek BEFORE INSERT ON Vypozicka FOR EACH ROW DECLARE v_pocet NUMBER; BEGIN SELECT Pocet_vypoziciek INTO v_pocet FROM Citatel WHERE ID_pouzivatela = :NEW.ID_pouzivatela AND Status = 'Aktívny'; IF v_pocet >= 4 THEN RAISE_APPLICATION_ERROR(-20001, 'Citatel uz ma 4 aktivnych vypoziciek.'); END IF; END; / -- Trigger 3: Update exemplar count in Titul table when a new exemplar is added or removed CREATE OR REPLACE TRIGGER trigger_pocet_exemplarov AFTER INSERT OR DELETE OR UPDATE OF ID_titulu ON Exemplar FOR EACH ROW BEGIN -- When a new copy is added IF INSERTING THEN UPDATE Titul SET Pocet_exemplarov = Pocet_exemplarov + 1 WHERE ID_titulu = :NEW.ID_titulu; -- When a copy is deleted ELSIF DELETING THEN UPDATE Titul SET Pocet_exemplarov = Pocet_exemplarov - 1 WHERE ID_titulu = :OLD.ID_titulu; -- When a copy's title is changed ELSIF UPDATING THEN -- Decrement count for old title UPDATE Titul SET Pocet_exemplarov = Pocet_exemplarov - 1 WHERE ID_titulu = :OLD.ID_titulu; -- Increment count for new title UPDATE Titul SET Pocet_exemplarov = Pocet_exemplarov + 1 WHERE ID_titulu = :NEW.ID_titulu; END IF; END; / -- Trigger 4: Update loan status to "Oneskorená" when return date passes CREATE OR REPLACE TRIGGER trigger_aktualizuj_stav_vypozicky BEFORE UPDATE OR INSERT ON Vypozicka FOR EACH ROW BEGIN -- If loan is active and planned return date has passed IF (:NEW.Stav = 'Aktívna' AND :NEW.Planovany_datum_vratenia < SYSDATE) THEN :NEW.Stav := 'Oneskorená'; END IF; END; / -- Procedure to manually check and update overdue loans CREATE OR REPLACE PROCEDURE aktualizuj_oneskorene_vypozicky AS v_count NUMBER; BEGIN UPDATE Vypozicka SET Stav = 'Oneskorená' WHERE Stav = 'Aktívna' AND Planovany_datum_vratenia < SYSDATE; v_count := SQL%ROWCOUNT; COMMIT; DBMS_OUTPUT.PUT_LINE('Aktualizácia oneskorených výpožičiek dokončená. Počet aktualizovaných záznamov: ' || v_count); EXCEPTION WHEN OTHERS THEN ROLLBACK; DBMS_OUTPUT.PUT_LINE('Chyba pri aktualizácii oneskorených výpožičiek: ' || SQLERRM); END; / -- *** 2. STORED PROCEDURES *** -- Procedure 1: Process a reservation CREATE OR REPLACE PROCEDURE spracuj_rezervaciu ( p_id_rezervacie IN NUMBER, p_days_to_return IN NUMBER DEFAULT 30 ) AS v_rezervacia Rezervacia%ROWTYPE; v_exemplar_id NUMBER; v_user_id NUMBER; v_title_id NUMBER; v_today DATE := SYSDATE; -- Custom exceptions e_rezervacia_not_found EXCEPTION; e_rezervacia_not_active EXCEPTION; e_no_copy_available EXCEPTION; BEGIN -- Get reservation details BEGIN SELECT * INTO v_rezervacia FROM Rezervacia WHERE ID_rezervacie = p_id_rezervacie; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE e_rezervacia_not_found; END; -- Check if reservation is active IF v_rezervacia.Stav != 'Aktívna' THEN RAISE e_rezervacia_not_active; END IF; v_user_id := v_rezervacia.ID_pouzivatela; v_title_id := v_rezervacia.ID_titulu; -- Find available copy BEGIN SELECT ID_exemplaru INTO v_exemplar_id FROM Exemplar WHERE ID_titulu = v_title_id AND Status = 'Dostupný' AND ROWNUM = 1; EXCEPTION WHEN NO_DATA_FOUND THEN RAISE e_no_copy_available; END; -- Start transaction SAVEPOINT start_transaction; BEGIN -- Create loan INSERT INTO Vypozicka ( ID_pouzivatela, ID_exemplaru, Datum_vypozicania, Planovany_datum_vratenia, Stav ) VALUES ( v_user_id, v_exemplar_id, v_today, v_today + p_days_to_return, 'Aktívna' ); -- Update copy status UPDATE Exemplar SET Status = 'Vypožičaný' WHERE ID_exemplaru = v_exemplar_id; -- Update reservation status UPDATE Rezervacia SET Stav = 'Vybavená' WHERE ID_rezervacie = p_id_rezervacie; COMMIT; DBMS_OUTPUT.PUT_LINE('Rezervácia úspešne spracovaná. Vytvorená výpožička pre používateľa ID: ' || v_user_id); EXCEPTION WHEN OTHERS THEN ROLLBACK TO start_transaction; DBMS_OUTPUT.PUT_LINE('Chyba pri spracovaní rezervácie: ' || SQLERRM); RAISE; END; EXCEPTION WHEN e_rezervacia_not_found THEN DBMS_OUTPUT.PUT_LINE('Rezervácia s ID ' || p_id_rezervacie || ' nebola nájdená.'); WHEN e_rezervacia_not_active THEN DBMS_OUTPUT.PUT_LINE('Rezervácia s ID ' || p_id_rezervacie || ' nie je aktívna.'); WHEN e_no_copy_available THEN DBMS_OUTPUT.PUT_LINE('Pre titul s ID ' || v_title_id || ' nie je dostupný žiadny exemplár.'); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Neočakávaná chyba: ' || SQLERRM); END; / -- Procedure 2: Generate overdue loans report with cursor CREATE OR REPLACE PROCEDURE generuj_report_oneskorene ( p_datum IN DATE DEFAULT SYSDATE ) AS -- Cursor declaration for overdue loans CURSOR c_oneskorene IS SELECT v.ID_vypozicky, v.Datum_vypozicania, v.Planovany_datum_vratenia, p.ID_pouzivatela, p.Meno, p.Priezvisko, t.ID_titulu, t.Nazov, ROUND(p_datum - v.Planovany_datum_vratenia) AS dni_po_termine FROM Vypozicka v JOIN Pouzivatel p ON v.ID_pouzivatela = p.ID_pouzivatela JOIN Exemplar e ON v.ID_exemplaru = e.ID_exemplaru JOIN Titul t ON e.ID_titulu = t.ID_titulu WHERE v.Stav IN ('Aktívna', 'Oneskorená') AND v.Planovany_datum_vratenia < p_datum ORDER BY dni_po_termine DESC; -- Variables v_record c_oneskorene%ROWTYPE; v_count NUMBER := 0; BEGIN DBMS_OUTPUT.PUT_LINE('=== REPORT ONESKORENÝCH VÝPOŽIČIEK K DÁTUMU ' || TO_CHAR(p_datum, 'DD.MM.YYYY') || ' ==='); DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------'); DBMS_OUTPUT.PUT_LINE('ID | POUŽÍVATEĽ | TITUL | DÁTUM VRÁTENIA | DNI PO TERMÍNE'); DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------'); -- Process cursor data OPEN c_oneskorene; LOOP FETCH c_oneskorene INTO v_record; EXIT WHEN c_oneskorene%NOTFOUND; v_count := v_count + 1; DBMS_OUTPUT.PUT_LINE( RPAD(v_record.ID_vypozicky, 3) || ' | ' || RPAD(v_record.Meno || ' ' || v_record.Priezvisko, 19) || ' | ' || RPAD(v_record.Nazov, 28) || ' | ' || RPAD(TO_CHAR(v_record.Planovany_datum_vratenia, 'DD.MM.YYYY'), 14) || ' | ' || LPAD(v_record.dni_po_termine, 13) ); END LOOP; CLOSE c_oneskorene; IF v_count = 0 THEN DBMS_OUTPUT.PUT_LINE('Žiadne oneskorené výpožičky.'); ELSE DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------------------------'); DBMS_OUTPUT.PUT_LINE('CELKOM ONESKORENÝCH VÝPOŽIČIEK: ' || v_count); END IF; EXCEPTION WHEN OTHERS THEN IF c_oneskorene%ISOPEN THEN CLOSE c_oneskorene; END IF; DBMS_OUTPUT.PUT_LINE('Chyba pri generovaní reportu: ' || SQLERRM); END; / -- *** 3. INDEXES AND EXPLAIN PLAN *** -- Complex query for loan statistics that will benefit from indexing EXPLAIN PLAN FOR SELECT p.ID_pouzivatela, p.Meno || ' ' || p.Priezvisko AS Pouzivatel, COUNT(v.ID_vypozicky) AS Pocet_vypoziciek, ROUND(AVG(NVL(v.Skutocny_datum_vratenia, SYSDATE) - v.Datum_vypozicania)) AS Priemerna_dlzka_vypozicky, COUNT(CASE WHEN v.Stav = 'Aktívna' THEN 1 END) AS Aktivne_vypozicky, COUNT(CASE WHEN v.Stav = 'Oneskorená' THEN 1 END) AS Oneskorene_vypozicky FROM Pouzivatel p JOIN Vypozicka v ON p.ID_pouzivatela = v.ID_pouzivatela JOIN Exemplar e ON v.ID_exemplaru = e.ID_exemplaru WHERE v.Datum_vypozicania > ADD_MONTHS(SYSDATE, -12) GROUP BY p.ID_pouzivatela, p.Meno, p.Priezvisko HAVING COUNT(v.ID_vypozicky) > 0; -- Display the execution plan before creating the index SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); -- Create indexes to optimize the query CREATE INDEX idx_vypozicka_pouzivatela ON Vypozicka(ID_pouzivatela); CREATE INDEX idx_vypozicka_datum ON Vypozicka(Datum_vypozicania); CREATE INDEX idx_vypozicka_stav ON Vypozicka(Stav); -- Run EXPLAIN PLAN again to see the improvement EXPLAIN PLAN FOR SELECT p.ID_pouzivatela, p.Meno || ' ' || p.Priezvisko AS Pouzivatel, COUNT(v.ID_vypozicky) AS Pocet_vypoziciek, ROUND(AVG(NVL(v.Skutocny_datum_vratenia, SYSDATE) - v.Datum_vypozicania)) AS Priemerna_dlzka_vypozicky, COUNT(CASE WHEN v.Stav = 'Aktívna' THEN 1 END) AS Aktivne_vypozicky, COUNT(CASE WHEN v.Stav = 'Oneskorená' THEN 1 END) AS Oneskorene_vypozicky FROM Pouzivatel p JOIN Vypozicka v ON p.ID_pouzivatela = v.ID_pouzivatela JOIN Exemplar e ON v.ID_exemplaru = e.ID_exemplaru WHERE v.Datum_vypozicania > ADD_MONTHS(SYSDATE, -12) GROUP BY p.ID_pouzivatela, p.Meno, p.Priezvisko HAVING COUNT(v.ID_vypozicky) > 0; -- Display the execution plan after creating the index SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY); -- *** 4. ACCESS RIGHTS *** GRANT SELECT ON Pouzivatel TO xpribik00; GRANT SELECT ON Citatel TO xpribik00; GRANT SELECT ON Zamestnanec TO xpribik00; GRANT SELECT ON Titul TO xpribik00; GRANT SELECT ON Kniha TO xpribik00; GRANT SELECT ON Casopis TO xpribik00; GRANT SELECT ON Exemplar TO xpribik00; GRANT SELECT, INSERT, UPDATE ON Rezervacia TO xpribik00; GRANT SELECT, INSERT, UPDATE ON Vypozicka TO xpribik00; GRANT SELECT, INSERT, UPDATE ON Recenzia TO xpribik00; -- Grant execute privileges on procedures GRANT EXECUTE ON spracuj_rezervaciu TO xpribik00; GRANT EXECUTE ON generuj_report_oneskorene TO xpribik00; GRANT EXECUTE ON aktualizuj_oneskorene_vypozicky TO xpribik00; -- *** 6. COMPLEX QUERY WITH WITH CLAUSE AND CASE *** -- Complex query using WITH clause and CASE operator to categorize readers WITH -- Subquery 1: Calculate loan statistics for users user_stats AS ( SELECT p.ID_pouzivatela, p.Meno || ' ' || p.Priezvisko AS Pouzivatel, COUNT(v.ID_vypozicky) AS Pocet_vypoziciek, COUNT(CASE WHEN v.Stav = 'Oneskorená' THEN 1 END) AS Pocet_oneskoreni, ROUND(AVG(NVL(v.Skutocny_datum_vratenia, SYSDATE) - v.Datum_vypozicania)) AS Priemerne_dni FROM Pouzivatel p LEFT JOIN Vypozicka v ON p.ID_pouzivatela = v.ID_pouzivatela JOIN Citatel c ON p.ID_pouzivatela = c.ID_pouzivatela GROUP BY p.ID_pouzivatela, p.Meno, p.Priezvisko ), -- Subquery 2: Calculate preferred genres for users user_genres AS ( SELECT v.ID_pouzivatela, t.Kategoria, COUNT(*) AS Pocet_vypoziciek_kategorie, RANK() OVER (PARTITION BY v.ID_pouzivatela ORDER BY COUNT(*) DESC) AS Poradia FROM Vypozicka v JOIN Exemplar e ON v.ID_exemplaru = e.ID_exemplaru JOIN Titul t ON e.ID_titulu = t.ID_titulu GROUP BY v.ID_pouzivatela, t.Kategoria ) -- Main query combining the CTE results with categorization using CASE SELECT us.Pouzivatel, us.Pocet_vypoziciek, CASE WHEN us.Pocet_vypoziciek = 0 THEN 'Neaktívny čitateľ' WHEN us.Pocet_vypoziciek BETWEEN 1 AND 3 THEN 'Príležitostný čitateľ' WHEN us.Pocet_vypoziciek BETWEEN 4 AND 10 THEN 'Aktívny čitateľ' ELSE 'Náruživý čitateľ' END AS Kategoria_citatela, CASE WHEN us.Pocet_oneskoreni = 0 THEN 'Vzorný' WHEN us.Pocet_oneskoreni = 1 THEN 'Občas mešká' WHEN us.Pocet_oneskoreni BETWEEN 2 AND 3 THEN 'Často mešká' ELSE 'Chronicky mešká' END AS Spolahlivost, us.Priemerne_dni AS Priemerna_doba_vypozicky, ug.Kategoria AS Preferovana_kategoria FROM user_stats us LEFT JOIN user_genres ug ON us.ID_pouzivatela = ug.ID_pouzivatela AND ug.Poradia = 1 ORDER BY us.Pocet_vypoziciek DESC, us.Pouzivatel; -- *** 7. DEMONSTRATION OF FUNCTIONALITY *** -- Demonstration of Trigger 1: Update loan count for reader (trigger_pocet_vypoziciek) -- First, check the current loan count for a reader SELECT c.ID_pouzivatela, p.Meno, p.Priezvisko, c.Pocet_vypoziciek FROM Citatel c JOIN Pouzivatel p ON c.ID_pouzivatela = p.ID_pouzivatela WHERE c.ID_pouzivatela = 1; -- Insert a new loan for the reader INSERT INTO Vypozicka (ID_pouzivatela, ID_exemplaru, Datum_vypozicania, Planovany_datum_vratenia, Stav) VALUES (1, 5, SYSDATE, SYSDATE + 30, 'Aktívna'); -- Check if the loan count was automatically updated by the trigger SELECT c.ID_pouzivatela, p.Meno, p.Priezvisko, c.Pocet_vypoziciek FROM Citatel c JOIN Pouzivatel p ON c.ID_pouzivatela = p.ID_pouzivatela WHERE c.ID_pouzivatela = 1; -- Demonstration of Trigger 2: Check maximum loan limit (trigger_maximalny_pocet_vypoziciek) -- For demonstration purposes, we update the count directly UPDATE Citatel SET Pocet_vypoziciek = 4 WHERE ID_pouzivatela = 2; BEGIN DBMS_OUTPUT.PUT_LINE('Pokus o vytvorenie piatej výpožičky pre čitateľa s ID 3:'); INSERT INTO Vypozicka (ID_pouzivatela, ID_exemplaru, Datum_vypozicania, Planovany_datum_vratenia, Stav) VALUES (2, 4, SYSDATE, SYSDATE + 30, 'Aktívna'); EXCEPTION WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Chyba: ' || SQLERRM); END; / -- Demonstration of Trigger 3: Update copy count SELECT t.ID_titulu, t.Nazov, t.Pocet_exemplarov FROM Titul t WHERE t.ID_titulu = 1; -- Insert a new copy INSERT INTO Exemplar (ID_titulu, Datum_nadobudnutia, Umiestnenie, Signatura, Status) VALUES (1, SYSDATE, 'Regál A, Polica 1', 'F-TOL-003', 'Dostupný'); -- Check if the count was updated SELECT t.ID_titulu, t.Nazov, t.Pocet_exemplarov FROM Titul t WHERE t.ID_titulu = 1; -- Demonstration of Trigger 4: Update loan status to "Oneskorená" INSERT INTO Vypozicka (ID_pouzivatela, ID_exemplaru, Datum_vypozicania, Planovany_datum_vratenia, Stav) VALUES (1, 1, SYSDATE, SYSDATE - 5, 'Aktívna'); -- Intentionally setting a past date -- Check if the loan status was updated automatically to 'Oneskorená' SELECT ID_vypozicky, Stav FROM Vypozicka WHERE ID_exemplaru = 1 AND ID_pouzivatela = 1; -- Run the procedure to update any other overdue loans BEGIN aktualizuj_oneskorene_vypozicky; END; / -- Demonstration of Procedure 1: Process a reservation -- First, create a new reservation INSERT INTO Rezervacia (ID_pouzivatela, ID_titulu, Datum_rezervacie, Datum_expiracie, Stav) VALUES (1, 3, SYSDATE, SYSDATE + 7, 'Aktívna'); -- Get the ID of the inserted reservation and process it DECLARE v_id NUMBER; BEGIN SELECT MAX(ID_rezervacie) INTO v_id FROM Rezervacia WHERE ID_pouzivatela = 1 AND ID_titulu = 3; -- Process the reservation spracuj_rezervaciu(v_id); END; / -- Demonstration of Procedure 2: Generate overdue loans report BEGIN generuj_report_oneskorene(SYSDATE); END; / -- Demonstration of Materialized View SELECT * FROM xpribik00.mv_aktivita_citatelov WHERE ID_pouzivatela = 1; -- Make changes that affect the view UPDATE Vypozicka SET Stav = 'Vrátená', Skutocny_datum_vratenia = SYSDATE WHERE ID_vypozicky = (SELECT MIN(ID_vypozicky) FROM Vypozicka WHERE ID_pouzivatela = 1 AND Stav = 'Aktívna'); -- View now shows updated data (after it's refreshed by xpribik00) SELECT * FROM xpribik00.mv_aktivita_citatelov WHERE ID_pouzivatela = 1; COMMIT;