Baza danych hotel Maciej Gerus 258583 Baza ta składa się z 7 tabel (jak widać na rysunku) i pozwala wirtualnie zapisywać (oraz analizować) niektóre uproszczone elementy pracy przedsiębiorstwa oferującego usługi noclegowe/gastronomiczne. Pomijając może opis samych tabel [patrz: rysunek] skupię się na dokładniejszym opisie zastosowanych funkcji, więzów, wyzwalaczy oraz kluczy. Kluczem głównym każdej tabeli jest komórka typu serial (zazwyczaj nazwana, wyjątek tabela goscie, gdyż praktyka taka kojarzy mi się jednoznacznie z obozem koncentracyjnym stad goście mają przypisane id również typu serial). Kolejnym wyjątkiem jest tabela pokoj, gdzie y mają typ integer. W tabelach tych pojawił się także szereg kluczy obcych przykładowo komórki danie1, danie4 z tabeli sniadanie przyjmują jedynie wartości z kolumny danie tabeli menu. Z kolei w kolumnie gość i pokoj tabeli pobyt pojawiają się tylko wartości z kolumn id tabeli goscie i tabeli pokoj, odpowiednio. Zastosowałem także kilka więzów typu check przykładowo komórka poczatek tabeli pobyt musi mieć mniejszą wartość niż komórka koniec, a w tabeli pokoj być zgodny z em piętra na którym ten pokój się znajduje (przykładowo na trzecim piętrze mogą znajdować się tylko pokoje 301, 302, 399). W moim projekcie użyłem również dwóch wyzwalaczy rezerwacja_trigger() na tabeli pobyt, który sprawdza [używając do tego podfunkcji zajęte(integer, date)] czy istnieje jakiś dzień kiedy pokój pożądany przez potencjalnego gościa jest już zajęty przez innego gościa. Jeśli zajdzie potrzeba uniemożliwia dokonanie kolidującej rezerwacji [aczkolwiek przymyka oko na pokrywające się rezerwacje tego samego klienta widzę tu pole do dalszych ulepszeń, które nie mają jednak istotnego znaczenia przy obecnym kształcie bazy]. Drugi wyzwalacz kontrola_kuliarna_trigger() zmienia zamówienie niesfornego klienta próbującego, wbrew naszym radom, zjeść grzanki z dżemem na poprawne zamówienie składające się z grzanek i marmolady. Na potrzeby tej bazy stworzyłem także funkcję sprzatanie(), która w czasie rzeczywistym sprawdza jak dawno sprzątane były wszystkie pokoje oraz sprząta te, dla których okres ten jest dłuższy niż jeden dzień. Sprzątanie polega na zmianie wartości komórki ostatnio_sprzatane danej brudnej krotki na obecną oraz przypisaniu pewnemu pracownikowi (w tabeli sprzatanie) pietra na którym leży dany pokój. Pracowników wybiera kierując się ilością już wysprzątanych pokoi najbardziej pracowitym przyporządkowuje te piętrz na których będą mieli najmniej roboty jednocześnie zliczając te przepracowane godziny [w komórce staż danego pracownika]. Z efektów pracy tej funkcji korzysta funkcja wypalata(integer) tworząca widok pokazujący ile należy wypłacić każdemu
pracownikowi za wykonaną pracę, przy zadanej stawce od pokoju. Kolejna tworzącą widok funkcją jest zamówienie_klienta(text), która pozwala zobaczyć jakie zestawy zamawiał w przeszłości klient którego nazwisko znamy (funkcja może mieszać klientów dwóch klientów o rożnych imionach i tych samych nazwiskach jednak w mojej przykładowej bazie, na poziomie teoretycznym, nazwisko jest kluczem głównym choć oczywiście łatwo może przestać być. Dodatkową atrakcją tej funkcji jest jej zdolność do rozróżniania z dużą dozą prawdopodobieństwa płci gościa (bazująca a obserwacji, że większość polskich imion kobiecych kończy się na a; znowu niedokładność ta nie ma znaczenia w bazie przykładowej, jej usuniecie jest zatem nieistotne na obecnym etapie rozwoju bazy). Również funkcja zamowienia(data) jeszcze nie działa [program nie pozwala mi nazwać widoku dyspozycje_do_kuchi_2014- - wywala sie przy - ], chociaż gdyby projekt miał zostać wdrożony praktycznie dostrzegam w niej duży potencjał.
opis menu imie nazwisko pokojowki danie danie1 danie2 sniadanie danie3 danie4 Schemat bazy danych Maciej Gerus 258583 baza hotel gosc poczatek pobyt koniec pokoj pietro2 pietro3 sprzatanie data staz pietro1 Ilosc_osob pietro nazwisko id goscie Ile_osob ostatnio_sprzatane pokoj imie adres Kolor oznacza do której tabeli przynależy dana kolumna, biała obwódka oznacza klucz główny, natomiast strzałka klucz obcy
--KOD SQL --UWAGA! KOD PRZEKLEJONY W CALOSCI MOZE NIE KOMPILOWAC SIE POPRAWNIE. POWSTAJA WOWCZAS NIEZIDENTYFIKOWANEGO PRZEZE MNIE POCHODZENIA PROBLEMY Z NIEDOMYKANIEM SIE NAWIASOW I APOSTROFOW KOD PRZEKLEJONY W CZESCIACH DZIALA NATOOMIAST POPRAWNIE. DROP TABLE pokoj CASCADE; CREATE TABLE pokoj ( INTEGER PRIMARY KEY, pietro INTEGER NOT NULL, ostatnio_sprzatane DATE, ilosc_osob INTEGER); DROP TABLE pobyt CASCADE; CREATE TABLE pobyt ( SERIAL, pokoj INTEGER NOT NULL, poczatek DATE NOT NULL, koniec DATE NOT NULL, gosc INTEGER NOT NULL, ilosc_osob INTEGER NOT NULL); DROP TABLE goscie CASCADE; CREATE TABLE goscie (id SERIAL, imie VARCHAR (255) NOT NULL, nazwisko VARCHAR (255) NOT NULL, adres VARCHAR (255) NOT NULL); DROP TABLE menu CASCADE; CREATE TABLE menu ( SERIAL, danie VARCHAR (255), opis VARCHAR (255)); DROP TABLE sniadanie CASCADE; CREATE TABLE sniadanie ( SERIAL, danie1 VARCHAR (255) NOT NULL, danie2 VARCHAR (255), danie3 VARCHAR (255), danie4 VARCHAR (255), data DATE NOT NULL, dla_kogo INTEGER NOT NULL, uwagi VARCHAR (255)); DROP TABLE pokojowki CASCADE; CREATE TABLE pokojowki ( SERIAL, imie VARCHAR (255) NOT NULL, nazwisko VARCHAR (255) NOT NULL, staz INTEGER NOT NULL); DROP TABLE sprzatanie CASCADE; CREATE TABLE sprzatanie ( SERIAL, pietro1 INTEGER, pietro2 INTEGER, pietro3 INTEGER, data DATE NOT NULL); INSERT INTO pokojowki (imie, nazwisko, staz) VALUES ('Celina', 'Czyscioszka', 7); INSERT INTO pokojowki (imie, nazwisko, staz) VALUES ('Maria', 'Zmiatacz', 17); INSERT INTO pokojowki (imie, nazwisko, staz) VALUES ('Grzegorz', 'Pucus', 2); INSERT INTO goscie (imie, nazwisko, adres) VALUES ('Ada', 'Niewporewpada', 'Mikolajowice 25 41-274'); INSERT INTO goscie (imie, nazwisko, adres) VALUES ('Maria', 'Turystyczna', 'Wroclaw ul.kosciuszki 22/125 50-227', NULL);
INSERT INTO goscie (imie, nazwisko, adres) VALUES ('Marek', 'Uszaty', 'Warszawa ul.hirszfelda 869 17-227'); INSERT INTO pobyt (pokoj, poczatek, koniec, gosc, ilosc_osob) VALUES (101, '2013-11-11', '2013-11-11', 3, 2); INSERT INTO pobyt (pokoj, poczatek, koniec, gosc, ilosc_osob) VALUES (102, '2012-12-21', '2012-12-22', 1, 1); insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (101, 1, '2014-01-16', 2); insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (102, 1, '2014-01-16', 1); insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (201, 2, '2014-01-16', 2); insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (301, 3, '2014-01-16', 4); insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (302, 3, '2014-01-16', 4); insert INTO menu (danie, opis ) VALUES ('marmolada', 'rozana, wieloowocowa lub konfitury malinowe'); insert INTO menu (danie, opis ) VALUES ('grzanki', 'najlepiej smakuja z marmolada'); insert INTO menu (danie) VALUES ('jajecznica' ); insert INTO menu (danie, opis ) VALUES ('platki sniadaniowe', ' + mleko'); insert INTO sniadanie (danie1, danie2, danie3, data, dla_kogo, uwagi ) VALUES ('marmolada', 'grzanki', 'platki sniadaniowe', '2013-11-11', 1, 'Dla dwoch osob'); insert INTO sniadanie (danie1, danie2, data, dla_kogo) VALUES ('platki sniadaniowe', 'grzanki','2012-12-22', 2); ALTER TABLE pobyt ADD FOREIGN KEY (pokoj) REFERENCES pokoj() ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE pobyt ADD FOREIGN KEY (gosc) REFERENCES goscie(id) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE sniadanie ADD FOREIGN KEY (danie1) REFERENCES menu(danie) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE sniadanie ADD FOREIGN KEY (danie2) REFERENCES menu(danie) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE sniadanie ADD FOREIGN KEY (danie3) REFERENCES menu(danie) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE sniadanie ADD FOREIGN KEY (dla_kogo) REFERENCES pobyt() ON DELETE ON UPDATE CASCADE; ALTER TABLE pobyt ADD CHECK ( poczatek < koniec); ALTER TABLE pokoj ADD CHECK ( >= pietro*100);
ALTER TABLE pobyt ADD CHECK ( <= pietro*100+99); --- dodac reszte --a tu dodaje wlasna funkcje ktora pieknie smiga w MySQL''u (czy tam SQLserwer?) a w POSTGR mi jej brakuje --update sa inne, rownie wygodne, trza bylo poszukac DROP FUNCTION DATE_ADD(character varying, integer, date); CREATE OR REPLACE FUNCTION DATE_ADD(character varying, integer, date) RETURNS DATE AS' DECLARE p_interval ALIAS FOR $1; p_n ALIAS FOR $2; p_date ALIAS FOR $3; if p_interval = ''m'' then return p_date + cast(p_n '' months'' as interval); elseif p_interval = ''y'' then return p_date + cast(p_n '' years'' as interval); elseif p_interval = ''d'' then return p_date + cast(p_n '' days'' as interval); else return null; end if; 'LANGUAGE 'plpgsql'; DROP FUNCTION zajete (INTEGER, DATE); CREATE OR REPLACE FUNCTION zajete (nr_pokoju INTEGER, data DATE) RETURNS INTEGER AS' RETURN ( SELECT gosc FROM pobyt WHERE ( (poczatek <= data ) AND (koniec > data ) AND (pokoj = nr_pokoju) ) ); ' LANGUAGE 'plpgsql'; CREATE OR REPLACE FUNCTION rezerwacja () RETURNS TRIGGER AS' DECLARE i INTEGER; v INTEGER;
i := 0; --DATEDIF(NEW.koniec, NEW.poczatek); WHILE (date_add(''d'', i, NEW.poczatek) < NEW.koniec) LOOP SELECT INTO v zajete( NEW.pokoj, DATE_ADD(''d'', i, NEW.poczatek)); IF ( v!= NEW.gosc) THEN RAISE EXCEPTION ''Pokoj zajey przepraszamy :(''; ELSE i:=i+1; END LOOP; RETURN NEW; ' LANGUAGE 'plpgsql'; DROP TRIGGER rezerwacja_trigger ON pobyt CASCADE; CREATE TRIGGER rezerwacja_trigger BEFORE INSERT OR UPDATE ON pobyt FOR EACH ROW EXECUTE PROCEDURE rezerwacja(); --INSERT INTO pobyt (pokoj, poczatek, koniec, gosc, ilosc_osob) VALUES (101, '2013-09-11', '2013-11-01', 3, 2); --INSERT INTO pobyt (pokoj, poczatek, koniec, gosc, ilosc_osob) VALUES (101, '2013-09-16', '2013-11-07', 2, 6); --ta druga juz nie pyknie --przydaloby sie jeszcze walnac zmiane pobyt.ostatni_pobyt, ale nie jestem przekonany do przechowywania tam mozliwie przyszlych wartosci CREATE OR REPLACE FUNCTION sprzatanie () returns VOID AS ' declare x RECORD; y RECORD; z INTEGER; v INTEGER; SELECT INTO x p.pietro, count (p.) as county FROM pokoj AS p WHERE (p.ostatnio_sprzatane + 1 < CURRENT_DATE ) GROUP BY p.pietro ORDER BY count(p.) DESC LIMIT 1; IF (x.county > 0) THEN INSERT INTO sprzatanie (pietro1, pietro2, pietro3, data) VALUES ( NULL, NULL, NULL, CURRENT_DATE );
SELECT INTO z () FROM sprzatanie ORDER BY data DESC LIMIT 1; WHILE (x.county > 0) LOOP SELECT INTO y w. FROM pokojowki AS w, sprzatanie AS s WHERE ( ( s. = z ) AND ( s.pietro1!= w. OR s.pietro1 IS NULL ) AND ( s.pietro2!= w. OR s.pietro2 IS NULL ) AND ( s.pietro3!= w. OR s.pietro3 IS NULL) ) ORDER BY w.staz ASC LIMIT 1; SELECT INTO v staz from pokojowki WHERE pokojowki. = y.; UPDATE pokojowki SET staz =(v + x.county ) WHERE pokojowki. = y.; UPDATE pokoj AS p SET ostatnio_sprzatane = CURRENT_DATE WHERE( pietro = x.pietro AND ( date_add (''d'', 2, p.ostatnio_sprzatane) < CURRENT_DATE ) ); IF (x IS NOT NULL) THEN EXECUTE ''UPDATE sprzatanie SET pietro'' x.pietro ''='' y. '' WHERE = '' z ''; ''; SELECT INTO x p.pietro, count (p.)as county FROM pokoj AS p WHERE ( p.ostatnio_sprzatane + 1 < CURRENT_DATE ) GROUP BY p.pietro ORDER BY count(p.) DESC LIMIT 1; END LOOP; 'LANGUAGE 'plpgsql'; --w koncu sie nie wywalilo --ale przy wywolaniu juz tak --po 3 godzinach odpaliłem już Execute Select * from pokoj; Select * from pokojowki; Select * from sprzatanie; Select sprzatanie(); Select * from pokoj; Select * from pokojowki; Select * from sprzatanie; insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (104, 1, '2014-01-16', 2); insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (105, 1, '2014-01-24', 1); insert INTO pokoj (, pietro, ostatnio_sprzatane, ilosc_osob) VALUES (303, 2, '2014-01-25', 6); DROP FUNCTION wyplata (stawka_godzinowa DOUBLE PRECISION)CASCADE; CREATE OR REPLACE FUNCTION wyplata (stawka_godzinowa DOUBLE PRECISION) RETURNS VOID AS '
DROP VIEW wyplaty; EXECUTE ''CREATE VIEW wyplaty AS SELECT nazwisko AS Nazwisko, imie AS Imie, '' stawka_godzinowa ''*staz AS Wynagrodzenie FROM pokojowki ORDER BY staz DESC;''; ' Language 'plpgsql'; DROP FUNCTION kontrola_kulinarna ()CASCADE; CREATE OR REPLACE FUNCTION kontrola_kulinarna () RETURNS TRIGGER AS ' IF (NEW.danie1 = ''grzanki'' OR NEW.danie2 = ''grzanki'' OR NEW.danie3 = ''grzanki'' OR NEW.danie4 = ''grzanki'' ) THEN IF (NEW.danie1 = ''dzem'') THEN RAISE WARNING ''dzem naprawde NIE PASUJE do grzanek! Zamiast niego podamy marmolade.''; NEW.danie1 :=''marmolada''; IF (NEW.danie2 = ''dzem'') THEN RAISE WARNING ''dzem naprawde NIE PASUJE do grzanek! Zamiast niego podamy marmolade.''; NEW.danie2 :=''marmolada''; IF (NEW.danie3 = ''dzem'') THEN RAISE WARNING ''dzem naprawde NIE PASUJE do grzanek! Zamiast niego podamy marmolade.''; NEW.danie3 :=''marmolada''; IF (NEW.danie4 = ''dzem'') THEN RAISE WARNING ''dzem naprawde NIE PASUJE do grzanek! Zamiast niego podamy marmolade.''; NEW.danie4 :=''marmolada''; RETURN NEW; ' Language 'plpgsql'; DROP TRIGGER kontrola_kulinarna_trigger ON sniadanie CASCADE; CREATE TRIGGER kontrola_kulinarna BEFORE INSERT OR UPDATE ON sniadanie FOR EACH ROW EXECUTE PROCEDURE kontrola_kulinarna(); DROP FUNCTION zamowienia_klienta (klient TEXT)CASCADE; CREATE OR REPLACE FUNCTION zamowienia_klienta (klient TEXT) RETURNS VOID AS ' --EXECUTE ''DROP VIEW dyspozycje_do_kuchni_'' data '' ;''; IF ( (SELECT imie FROM goscie WHERE nazwisko = klient) LIKE ''%a'') THEN EXECUTE ''CREATE VIEW dyspozycje_pani_'' imie ''_'' nazwisko '' AS
SELECT p. as Wizyta s.danie1 AS Przystawka, s.danie2 AS Danie_Glowne, s.danie3 AS Deser FROM sniadanie as s, goscie as g, pobyt as p WHERE (g.nazwisko = klient AND p.gosc=g.id AND p.=s.dla_kogo) ORDER BY p.pokoj ASC; SELECT dyspozycje_pana_'' imie ''_'' nazwisko '';''; ELSE EXECUTE ''CREATE VIEW dyspozycje_pana_'' imie ''_'' nazwisko '' AS SELECT p. as Wizyta s.danie1 AS Przystawka, s.danie2 AS Danie_Glowne, s.danie3 AS Deser FROM sniadanie as s, goscie as g, pobyt as p WHERE (g.nazwisko = klient AND p.gosc=g.id AND p.=s.dla_kogo) ORDER BY p.pokoj ASC; SELECT dyspozycje_pana_'' imie ''_'' nazwisko '';'' ; ' Language 'plpgsql'; DROP FUNCTION zamowienia (data DATE)CASCADE; CREATE OR REPLACE FUNCTION zamowienia (data DATE) RETURNS VOID AS ' --EXECUTE ''DROP VIEW dyspozycje_do_kuchni_'' data '' ;''; EXECUTE ''CREATE VIEW dyspozycje_do_kuchni_'' data '' AS SELECT g.nazwisko AS Nazwisko, g.imie AS Imie, s.danie1 AS Przystawka, s.danie2 AS Danie_Glowne, s.danie3 AS Deser FROM sniadanie as s, goscie as g, pobyt as p GROUP BY p.pietro; ORDER BY p.pokoj ASC;''; ' Language 'plpgsql';