Bazy danych - Politechnika Gdańska
Transkrypt
Bazy danych - Politechnika Gdańska
Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” Instrukcja do laboratorium z przedmiotu: Bazy danych Laboratorium nr 3. Metody zarządzania i analizy danych Opracował A. Bujnowski 2010-03-08 Projekt „Przygotowanie i realizacja kierunku inżynieria biomedyczna – studia międzywydziałowe” współfinansowany ze środków Unii Europejskiej w ramach Europejskiego Funduszu Społecznego. Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” 1. Cele laboratorium – zapoznanie się z metodami wprowadzania, usuwania i modyfikacji danych. Instrukcje warunkowe. Zapytania złożone i funkcje agregacji 2. Przykładowa baza danych: Jako przykład bazy danych posłuży nam przygotowana uprzednio i nieco zmodyfikowana struktura bazy dla wypożyczalni płyt DVD. Na potrzeby dzisiejszego laboratorium do poprzednio zaprojektowanej struktury tabel dołączymy jeszcze jedną: gatunek oraz dodamy atrybut cena dla każdej płyty. Zakładamy, że płyta należy do jednego gatunku. Kod w języku SQL zakładający bazę danych podana jest poniżej, wytłuszczonym drukiem pokazano zmiany w stosunku do poprzedniej wersji CREATE TABLE klient ( imie varchar(20) not null, nazwisko varchar(40) not null, nr_dowodu char(10), id_klienta serial primary key); CREATE TABLE gatunek( nazwa varchar(30) not null, id_gatunku serial PRIMARY KEY ); CREATE TABLE plyta( tytul varchar(40) not null, numer serial primary key, cena numeric(4,2), gatunek integer REFERENCES gatunek ON DELETE SET NULL ON UPDATE CASCADE); CREATE TABLE wypozyczenie( kto_wypozyczyl int not null REFERENCES klient ON DELETE RESTRICT ON UPDATE RESTRICT, 2 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” co_wypozyczyl int not null REFERENCES plyta ON DELETE RESTRICT ON UPDATE CASCADE, data_wypozyczenia timestamp default now(), data_zwrotu timestamp, primary key(kto_wypozyczyl, co_wypozyczyl, data_wypozyczenia) ); CREATE TABLE jest_pracownikiem( rabat int, id_klienta int primary key references klient); Dla ujednolicenia sposobu pracy w tym ćwiczeniu, po zalogowaniu do serwera bazy.eti.pg.gda.pl usuń poprzednią bazę poleceniem: dropdb lab2_login gdzie login to Twój login do systemu. Następnie załóż nową bazę o nazwie jak poprzednio poleceniem: createdb lab2_login Teraz przygotuj plik tekstowy z definicją poleceń zakładających bazę danych: mcedit baza3.sql Wypełnij zawartość tego pliku poleceniami z zaprezentowanego wyżej listingu. Połącz się ze swoją bazą danych: psql lab2_login Wczytaj instrukcje z pliku baza1.sql: \i baza3.sql lab2_bujnows=# \i baza3.sql psql:baza3.sql:5: NOTICE: CREATE TABLE will create implicit sequence "klient_id_klienta_seq" for "serial" column "klient.id_klienta" psql:baza3.sql:5: NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "klient_pkey" for table "klient" CREATE TABLE psql:baza3.sql:9: NOTICE: CREATE TABLE will create implicit sequence "plyta_numer_seq" for "serial" column "plyta.numer" psql:baza3.sql:9: NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "plyta_pkey" for table "plyta" CREATE TABLE psql:baza3.sql:12: ERROR: syntax error at or near "cena" at character 2 3 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” psql:baza3.sql:19: NOTICE: CREATE TABLE / PRIMARY KEY will create index "wypozyczenie_pkey" for table "wypozyczenie" CREATE TABLE psql:baza3.sql:23: NOTICE: CREATE TABLE / PRIMARY KEY will create index "jest_pracownikiem_pkey" for table "jest_pracownikiem" CREATE TABLE psql:baza3.sql:28: NOTICE: CREATE TABLE will create implicit "gatunek_id_gatunku_seq" for "serial" column "gatunek.id_gatunku" psql:baza3.sql:28: NOTICE: CREATE TABLE / PRIMARY KEY will create index "gatunek_pkey" for table "gatunek" CREATE TABLE lab2_bujnows=# implicit implicit sequence implicit W tej chwili struktura Twojej bazy danych jest gotowa do pracy. Na początku poznamy kilka poleceń na uzupełnienie jej danymi. Na początku jednak sprawdź, czy twoja baza przechowuje właściwą strukturę tabel: \d List of relations Schema | Name | Type | Owner --------+------------------------+----------+--------public | gatunek | table | bujnows public | gatunek_id_gatunku_seq | sequence | bujnows public | jest_pracownikiem | table | bujnows public | klient | table | bujnows public | klient_id_klienta_seq | sequence | bujnows public | plyta | table | bujnows public | plyta_numer_seq | sequence | bujnows public | wypozyczenie | table | bujnows (8 rows) \d klient Table "public.klient" Column | Type | Modifiers ------------+----------------------+---------------------------------------------------------------imie | character varying(20) | not null nazwisko | character varying(40) | not null nr_dowodu | character(10) | id_klienta | integer | not null default nextval('public.klient_id_klienta_seq'::text) Indexes: "klient_pkey" primary key, btree (id_klienta) \d plyta Table "public.plyta" Column | Type | Modifiers ---------+----------------------+---------------------------------------------------------tytul | character varying(40) | not null numer | integer | not null nextval('public.plyta_numer_seq'::text) cena | numeric(4,2) | gatunek | integer | Indexes: 4 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski default Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” "plyta_pkey" primary key, btree (numer) Foreign-key constraints: "$1" FOREIGN KEY (gatunek) REFERENCES gatunek(id_gatunku) \d gatunek Table "public.gatunek" Column | Type | Modifiers ------------+----------------------+----------------------------------------------------------------nazwa | character varying(30) | not null id_gatunku | integer | not null default nextval('public.gatunek_id_gatunku_seq'::text) Indexes: "gatunek_pkey" primary key, btree (id_gatunku) \d jest_pracownikiem Table "public.jest_pracownikiem" Column | Type | Modifiers ------------+---------+----------rabat | integer | id_klienta | integer | not null Indexes: "jest_pracownikiem_pkey" primary key, btree (id_klienta) Foreign-key constraints: "$1" FOREIGN KEY (id_klienta) REFERENCES klient(id_klienta) \d wypozyczenie Table "public.wypozyczenie" Column | Type | Modifiers ----------------+-----------------------------+-----------------------kto_wypozyczyl | integer | not null co_wypozyczyl | integer | not null d_wypozyczenia | timestamp without time zone | not null default now() d_zwrotu | timestamp without time zone | Indexes: "wypozyczenie_pkey" primary key, btree (kto_wypozyczyl, co_wypozyczyl, d_wypozyczenia) Foreign-key constraints: "$1" FOREIGN KEY (kto_wypozyczyl) REFERENCES klient(id_klienta) ON UPDATE RESTRICT ON DELETE RESTRICT "$2" FOREIGN KEY (co_wypozyczyl) REFERENCES plyta(numer) ON UPDATE CASCADE ON DELETE RESTRICT Jeżeli którakolwiek z tabel nie została założona lub jej struktura (poza właścicielem tabeli owner) jest inna niż ta, pokazana w instrukcji dokonaj niezbędnych poprawek. Może się przydać polecenie DROP TABLE lub ALTER TABLE. 3. Wstawianie danych do tabel. Na poprzednich zajęciach wprowadzone instrukcja wprowadzania danych do bazy danych INSERT INTO tabela VALUES. Sprawdźmy jeszcze raz jak zadziałają poniższe instrukcje: 5 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” INSERT INTO klient (imie,nazwisko,nr_dowodu) VALUES ('Jan','Kowalski','DB230398'); Sprawdź również uproszczoną metodę wprowadzania danych do takiej tabeli: INSERT INTO klient VALUES ('Anna','Nowak'); INSERT INTO klient VALUES ('Ewa','Zielińska','DB232343'); Możliwe jest też dodawanie z wymuszeniem własnej wartości id_klienta – ale jest to niezalecane. Sprawdź dlaczego. Odpowiedzią niech będzie poniższy fragment kodu: INSERT INTO klient (imie,nazwisko,nr_dowodu,id_klienta) VALUES ('Jan','Kowal','DB130398',1); Powyższe zapytanie nie powiedzie się, gdyż próbujemy użyć istniejącego id_klienta ponownie. INSERT INTO klient (imie,nazwisko,nr_dowodu,id_klienta) VALUES ('Jan','Kowal','DB130398',1); ERROR: duplicate key violates unique constraint "klient_pkey" Ale możemy poprawnie dodać klienta, z innym id_klienta niż już użyte: INSERT INTO klient (imie,nazwisko,nr_dowodu,id_klienta) VALUES ('Janusz','Kowalski','DB210398',4); INSERT INTO klient (imie,nazwisko,nr_dowodu,id_klienta) ('Janusz','Kowalski','DB210398',4); VALUES Problem pojawi się przy próbie dodania nowego klienta z automatycznym id_klienta: INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Maria','Kowalska','DB230300'); lab2_bujnows=# INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Maria','Kowalska','DB230300'); ERROR: duplicate key violates unique constraint "klient_pkey" lab2_bujnows=# select * from klient; imie | nazwisko | nr_dowodu | id_klienta --------+-----------+------------+-----------Jan | Kowalski | DB230398 | 1 Anna | Nowak | | 2 Ewa | Zielińska | DB232343 | 3 Janusz | Kowalski | DB210398 | 4 (4 rows) VALUES VALUES Ale w tym przypadku ponowne wykonanie tego samego polecenia już zadziała poprawnie (bo system przyjmie id_klienta jako 5): 6 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” lab2_bujnows=# INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Maria','Kowalska','DB230300'); INSERT 80519489 1 lab2_bujnows=# select * from klient; imie | nazwisko | nr_dowodu | id_klienta --------+-----------+------------+-----------Jan | Kowalski | DB230398 | 1 Anna | Nowak | | 2 Ewa | Zielińska | DB232343 | 3 Janusz | Kowalski | DB210398 | 4 Maria | Kowalska | DB230300 | 5 (5 rows) VALUES lab2_bujnows=# Dlatego należy zachowywać ostrożność przy wpisywaniu wartości do kolumn o typie serial lub auto_increment (MySQL). W przypadku konieczności załadowania do bazy danych większej ilości danych przydatne może być wykorzystanie skryptów podobnych do tych, które wykorzystywane były przy zakładaniu bazy danych. Opuść bazę danych (polecenie \q) i wyedytuj plik tekstowy jak załączono poniżej: mcedit klienci.sql INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Adam','Wisniak','AFF456432'); INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Adrian','Wicki','AFF456433'); INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Iwona','Wisniak','AFF456434'); INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Jolanta','Ponicka','AFF456452'); INSERT INTO klient (imie,nazwisko,nr_dowodu) ('Adam','Brzeski','AFG456432'); INSERT INTO klient(imie,nazwisko,nr_dowodu) ('Adam','Irenowski','AFF400432'); INSERT INTO klient(imie,nazwisko,nr_dowodu) ('Jolanta','Wisniak','AFF454432'); Zapisz plik (F2) i opuść edytor (F10). Połącz się ze swoją bazą: psql lab2_login Wczytaj skrypt zewnętrzny poleceniem \i klienci.sql. 7 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski VALUES VALUES VALUES VALUES VALUES VALUES VALUES Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” lab2_bujnows=# \i klienci.sql INSERT 80519490 1 INSERT 80519491 1 INSERT 80519492 1 INSERT 80519493 1 INSERT 80519494 1 INSERT 80519495 1 INSERT 80519496 1 lab2_bujnows=# select * from klient; imie | nazwisko | nr_dowodu | id_klienta ---------+-----------+------------+-----------Jan | Kowalski | DB230398 | 1 Anna | Nowak | | 2 Ewa | Zielińska | DB232343 | 3 Janusz | Kowalski | DB210398 | 4 Maria | Kowalska | DB230300 | 5 Adam | Wisniak | AFF456432 | 6 Adrian | Wicki | AFF456433 | 7 Iwona | Wisniak | AFF456434 | 8 Jolanta | Ponicka | AFF456452 | 9 Adam | Brzeski | AFG456432 | 10 Adam | Irenowski | AFF400432 | 11 Jolanta | Wisniak | AFF454432 | 12 (12 rows) Jak widać możliwe jest stosunkowo szybkie wczytywanie takich danych. Czasami jednak problematyczne może okazać się wyświetlanie potwierdzeń wczytywanych danych, lub konieczność przygotowania pliku z kompletną składnią w SQL-u. Większość systemów zarządzania bazami danych przychodzi tutaj z pomocą udostępniając interfejs do pobierania danych z pliku. Plik powinien być jednak odpowiednio sformatowany. W PostgreSQL poleceniem takim jest \copy: Copy działa w obie strony – pozwala na eksport danych z tabeli do pliku (COPY TO) i z pliku do tabeli (COPY FROM). W tej chwili zajmiemy się tylko importem danych do bazy danych. Plik, który zawiera dane do wgrania powinien być odpowiednio przygotowany. Wgrywając dane do tabeli musi on składać się z wierszy, z których każde pole zawiera dokładnie taką samą ilość separatorów pól. Dwa kolejne separatory pól oznaczają wartość typu NULL. Ostatnia linia nie może składać się z pustych znaków itd. Domyślnym separatorem pól jest znak tabulacji, zaś wierszy – znak końca linii. Przygotujmy zatem kilka plików wsadowych do wypełnienia gatunków i płyt: W tym celu opuść bazę danych (\q) i przygotuj pliki: mcedit gatunki.txt Zawartość pliku: komedia dramat musical kreskowka thriller sensacyjny przygodowy 8 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” historyczny fantasy mcedit plyty.txt Zawartość pliku: Shrek 15 Piraci z Karaibow 20 Calineczka 15 Szeregowiec Dolot 10 Constantine 20 Hellboy 15 UWAGA ! Ważne jest aby w pliku plyty.txt pomiędzy tytułem a ceną cisnąć TAB a nie kilka spacji !!! Teraz spróbuj wczytać tak przygotowane dane z pliku. lab2_bujnows=# \copy gatunek(nazwa) from gatunki.txt lab2_bujnows=# select * from gatunek; nazwa | id_gatunku -------------+-----------komedia | 1 dramat | 2 musical | 3 kreskówka | 4 thriller | 5 sensacyjny | 6 przygodowy | 7 historyczny | 8 fantasy | 9 (8 rows) lab2_bujnows=# lab2_bujnows=# \copy plyta(tytul,cena) from plyty.txt lab2_bujnows=# select * from plyta; tytul | numer | cena | gatunek -------------------+-------+-------+--------Shrek | 1 | 15.00 | Piraci z Karaibów | 2 | 20.00 | Calineczka | 3 | 15.00 | Szeregowiec Dolot | 4 | 10.00 | Constantine | 5 | 20.00 | Hellboy | 6 | 15.00 | (6 rows) 4. Aktualizacja danych w tabeli. W tak przygotowanej bazie danych pojawili się klienci, płyty i gatunki. lab2_bujnows=# select * from klient; 9 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” imie | nazwisko | nr_dowodu | id_klienta ---------+-----------+------------+-----------Jan | Kowalski | DB230398 | 1 Anna | Nowak | | 2 Ewa | Zielińska | DB232343 | 3 Janusz | Kowalski | DB210398 | 4 Maria | Kowalska | DB230300 | 5 Adam | Wisniak | AFF456432 | 6 Adrian | Wicki | AFF456433 | 7 Iwona | Wisniak | AFF456434 | 8 Jolanta | Ponicka | AFF456452 | 9 Adam | Brzeski | AFG456432 | 10 Adam | Irenowski | AFF400432 | 11 Jolanta | Wisniak | AFF454432 | 12 (12 rows) lab2_bujnows=# select * from plyta; tytul | numer | cena | gatunek -------------------+-------+-------+--------Shrek | 1 | 15.00 | Piraci z Karaibów | 2 | 20.00 | Calineczka | 3 | 15.00 | Szeregowiec Dolot | 4 | 10.00 | Constantine | 5 | 20.00 | Hellboy | 6 | 15.00 | (6 rows) lab2_bujnows=# select * from gatunek; nazwa | id_gatunku -------------+-----------komedia | 1 dramat | 2 musical | 3 kreskówka | 4 thriller | 5 sensacyjny | 6 przygodowy | 7 historyczny | 8 fantasy | 9 (9 rows) lab2_bujnows=# Płyty nie osiadają przypisanych gatunków. Spróbujmy zatem przypisać gatunki do płyt. Dokonamy tego przy pomocy polecenia UPDATE. Ustawmy zatem gatunek „komedia” dla płyty „Shrek”. To co trzeba zrobić to sprawdzić id gatunku dla komedii i wpisać to do bazy danych: lab2_bujnows=# select * from gatunek; nazwa | id_gatunku -------------+-----------komedia | 1 dramat | 2 musical | 3 10 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” kreskówka thriller sensacyjny przygodowy historyczny fantasy (9 rows) | | | | | | 4 5 6 7 8 9 lab2_bujnows=# select * from gatunek where nazwa='komedia'; nazwa | id_gatunku ---------+-----------komedia | 1 (1 row) lab2_bujnows=# update plyta set gatunek=1 where tytul='Shrek'; UPDATE 1 lab2_bujnows=# select * from plyta; tytul | numer | cena | gatunek -------------------+-------+-------+--------Piraci z Karaibów | 2 | 20.00 | Calineczka | 3 | 15.00 | Szeregowiec Dolot | 4 | 10.00 | Constantine | 5 | 20.00 | Hellboy | 6 | 15.00 | Shrek | 1 | 15.00 | 1 (6 rows) lab2_bujnows=# Aby wykonać to zadanie musieliśmy wykonać dwa zapytania – w pierwszym (SELECT) należało sprawdzić id_gatunku dla komedii i drugie polecenie jest faktycznym uzupełnieniem danych (UPDATE). Można zapisać to przy pomocy pojedynczego zapytania. Sprawdźmy to: update plyta set gatunek=(select id_gatunku 'kreskowka') where tytul = 'Calineczka'; from gatunek where nazwa W podobny sposób przypisz wszystkie filmy do gatunków: Piraci z Karaibów – przygodowy Szeregowiec Dolot – kreskówka Constantine - thriller Hellboy - fantasy Zastanów się czy w podobny sposób nie da się zapisać wypożyczeń poleceniem INSERT. Spróbuj zapisać akcję wypożyczenia płyty o tytule Constantine dla pana Jana Kowalskiego. W podobny sposób wypożycz płyty użytkownikom: Adrian Wicki Iwona Wisniak Jolanta Ponicka : : : Hellboy Szeregowiec Dolot Constantine Operatory w SQL 11 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski = Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” W języku SQL istnieje szereg operatorów warunkowych do używania z klauzulą WHERE. Są to: Operatory logiczne AND i OR , używane do połączenia warunków. Przykład : SELECT * FROM klient where id_klienta > 5 AND id_klienta < 10; Operatory porównania: > (większe), < (mniejsze) , <= (mniejsze lub równe), >= (większe lub równe), <> (różne) W przypadku ciągów tekstowych można wyróżnić operator LIKE, który pozwala na używanie znaków typu wildcard. Znaki te to '_' - zastępuje dowolny jeden znak oraz '%' - zastępuje dowolny ciąg znaków. Przykład: SELECT * FROM klient WHERE imię LIKE 'Ja%'; Operator BETWEEN .. AND … - poznamy go poprzez przykład użycia: SELECT * FROM klient where id_klienta BETWEEN 3 AND 7; Operator IN() pozwala wybrać elementy z listy do porównania wartości: SELECT * FROM klient WERE id IN(3,5,9); SELECT * FROM klient WERE imie IN('Jan','Janina'); Operator IS pozwal na sprawdzenie, czy dana wartość jest NULL lub nie jest: SELECT * FROM wypozyczenie WHERE data_zwrotu IS NULL; SELECT * FROM wypozyczenie WHERE data_zwrotu IS NOT NULL; Operatory i klauzulę WHERE można stosować w instrukcjach SELECT, UPDATE lub DELETE do wybierania zakresu rekordów na których dokonywane będą przewidziane operacje. Dzięki operatorom AND i OR można budować bardzo wyszukane warunki. Zapytania złożone i widoki. Przypomnijmy sobie zapytania złożone z wielu tabel, które przerabialiśmy podczas ostatnich zajęć. Utrwalmy te zapytania w formie widoków. SELECT k.imie, k.nazwisko, p.tytul,w.data_wypozyczenia, w.data_zwrotu FROM klient k, wypozyczenie w, plyta p 12 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” WHERE k.id_klienta = w.kto_wypozyczyl AND p.numer = w.co_wypozyczyl; SELECT k.imie, k.nazwisko, p.tytul,w.data_wypozyczenia, w.data_zwrotu FROM klient k LEFT JOIN wypozyczenie w ON k.id_klienta = w.kto_wypozyczyl LEFT JOIN plyta p ON p.numer = w.co_wypozyczyl; oraz SELECT k.imie, k.nazwisko, p.tytul,w.data_wypozyczenia, w.data_zwrotu FROM klient k JOIN wypozyczenie w ON k.id_klienta = w.kto_wypozyczyl RIGHT JOIN plyta p ON p.numer = w.co_wypozyczyl; Ale struktura naszej Bazy danych jest dalece bardziej rozbudowana niż – spróbujmy zastem wyświetlić zdecydowanie więcej danych. Zanim jednak to uczynimy dodajmy kila rekordów do tablicy jest pracownikiem. Pracownicy w naszej wypożyczalni to Jan Kowalski – przysługuje mu rabat 50 % i Adam Brzeski – z rabatem 40 %. Zatem dodajmy odpowiednie wpisy do tabeli jest_pracownikiem: INSERT INTO jest_pracownikiem(rabat,id_klienta) VALUES((SELECT id_klienta from klient where imie='Jan' AND nazwisko = 'Kowalski'),50); Podobnie zrób dla pana Adama Brzeskiego – z rabatem 40%. Teraz przepisz zapytanie wynikową w postaci: w imię | nazwisko | tytuł data_zwrotu | cena | rabat taki | sposób, gatunek | żeby pokazało tabelę data_wypozyczenia | Łatwo zauważyć, że w pojedynczym zapytaniu z wykorzystaniem klauzuli WHERE jako warunku łączącego tabele. W wyniku tak postawionego zapytania otrzymamy jedynie wiersze dla wszystkich wypożyczonych płyt które mają gatunek i są wypożyczone przez klienta będącego pracownikiem: SELECT k.imie, k.nazwisko, p.tytul, g.nazwa, w.data_wypozyczenia, w.data_zwrotu , p.cena, j.rabat FROM klient k, plyta p, gatunek g, wypozyczenie w, jest_pracownikiem j WHERE k.id_klienta = w.kto_wypozyczyl and p.numer = w.co_wypozyczyl and k.id_klienta = j.id_klienta and p.gatunek = g.id_gatunku; 13 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” Przyjrzyjmy się instrukcji warunkowej po klauzuli where: k.id_klienta = w.kto_wypozyczyl and (prawdziwe dla wszystkich wypożyczeń) p.numer = w.co_wypozyczyl and (prawdziwe dla wszystkich wypożyczeń) k.id_klienta = j.id_klienta and (prawdziwe tylko dla pracowników) p.gatunek = g.id_gatunku;(prawdziwe tylko dla płyt z wypełnionym gatunkiem) Widać zatem, że część zapytania z komentarzem na czerwono znacznie ograniczy ilość wyświetlanych wyników. Musimy zatem skorzystać z innej postaci połączenia tabel – przy pomocy JOIN. Sprawdźmy zatem jak zadziała połączenie join dla tych 5 tabel: SELECT k.imie, k.nazwisko, p.tytul, g.nazwa, w.data_wypozyczenia, w.data_zwrotu , p.cena, j.rabat FROM jest_pracownikiem j JOIN klient k ON k.id_klienta = j.id_klienta JOIN wypozyczenie w ON k.id_klienta = w.kto_wypozyczyl JOIN plyta p ON p.numer = w.co_wypozyczyl JOIN gatunek g ON p.gatunek = g.id_gatunku; Jak to działa i jaka jest kolejność złączeń? Sprawdźmy to na następującym rysunku, gdzie odpowiednio j oznacza tabelę jest_pracownikiem, k – tabelę klient, w – tabelę wypozyczeie, p – tabelę plyta i g tabelę gatunek: j k w j g Kolejne owale oznaczają tutaj kolejność łączeń. Jako pierwsze połączenie występuje tutaj połączenie klienta z jest_pracownikiem. Tabela jest_pracownikiem występuje po lewej stronie związku, ale powinniśmy w wyniku zapytania wyświetlić wszystkich klientów – nawet jeśli nie są pracownikami. Chronimy zatem prawą stronę związku tabel klient i jest_pracownikiem. Sprawdźmy jak zadziała ta część zapytania: SELECT k.imie, k.nazwisko, j.rabat from jest_pracownikiem j JOIN klient k ON k.id_klienta = j.id_klienta; SELECT k.imie, k.nazwisko, j.rabat from jest_pracownikiem j RIGHT JOIN klient k ON k.id_klienta = j.id_klienta; Następną w kolejności złączeń tabelą jest wypożyczenie – dołączmy ją do poprzedniego zapytania: 14 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” SELECT k.imie, k.nazwisko, j.rabat, w.data_wypozyczenia, w.data_zwrotu from jest_pracownikiem j RIGHT JOIN klient k ON k.id_klienta = j.id_klienta JOIN wypozyczenie w ON w.kto_wypozyczyl = k.id_klienta; Łatwo zauważyć, że taki JOIN zepsuje wynik RIGHT JOIN'a z poprzedniego zapytania. Ponieważ to co chcemy chronić jest po lewej stronie związku – musimy skorzystać z LEFT JOIN w kolejnym złączeniu. Sprawdźmy to: SELECT k.imie, k.nazwisko, j.rabat, w.data_wypozyczenia, w.data_zwrotu from jest_pracownikiem j RIGHT JOIN klient k ON k.id_klienta = j.id_klienta LEFT JOIN wypozyczenie w ON w.kto_wypozyczyl = k.id_klienta; Podobnie dołączmy płytę i gatunek: SELECT k.imie, k.nazwisko, p.tytul, g.nazwa, w.data_wypozyczenia, w.data_zwrotu , p.cena, j.rabat FROM jest_pracownikiem j RIGHT JOIN klient k ON k.id_klienta = j.id_klienta LEFT JOIN wypozyczenie w ON k.id_klienta = w.kto_wypozyczyl LEFT JOIN plyta p ON p.numer = w.co_wypozyczyl LEFT JOIN gatunek g ON p.gatunek = g.id_gatunku; Utrwalmy podobne do powyższego zapytanie w formie widoku: CREATE VIEW raport1 AS SELECT k.imie || ' ' || k.nazwisko, p.tytul, g.nazwa, w.data_wypozyczenia, w.data_zwrotu , p.cena, j.rabat FROM jest_pracownikiem j RIGHT JOIN klient k ON k.id_klienta = j.id_klienta LEFT JOIN wypozyczenie w ON k.id_klienta = w.kto_wypozyczyl LEFT JOIN plyta p ON p.numer = w.co_wypozyczyl LEFT JOIN gatunek g ON p.gatunek = g.id_gatunku; Użyliśmy tutaj operatora łączenia ciągów znaków ||. Pozwala to na połączenie np. imienia i nazwiska klienta tak, aby znalazły się w jednej kolumnie. Dodatkowo wymuszamy zmianę nazwy wyświetlanej kolumny dyrektywą AS. Funkcje agregacji W poprzednim ćwiczeniu pokazana została funkcja, dzięki której można dokonać połączenia ciągu znaków. Istnieje szereg innych funkcji – min matematycznych. Sprawdźmy działanie niektórych z 15 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” nich: SELECT SELECT SELECT SELECT 2+2; sin(1); log(numer) FROM plyta; sin(cena) FROM plyta; Ale istnieją także inne funkcje: SELECT SELECT SELECT SELECT SELECT count(*) FROM gatunek; count(imie) FROM klient; max(id_klienta) FROM klient; min(id_klienta) FROM klient; avg(id_klienta) FROM klient; Te funkcje w odróżnieniu od poprzednich zwracają pojedynczy wynik a operują na grupach krotek. Funkcje takie noszą miano funkcji agregacji, gdyż dokonują analizy na grupie krotek. Gdy wykonamy funkcję sin(id_klienta) to zwróci ona tyle wyników na ilu krotkach się ona wykonała, natomiast funkcje takie jak max(), min(), avg(), variance(), stdev() czy count() zwrócą pojedynczy wynik. Z tymi funkcjami związana jest dodatkowa klauzula zapytania SELECT : GROUP BY. GROUP BY powoduje przestawienie wyników w taki sposób, aby stanowiły one „przegrupowaną” tabelę względem kryterium. Na tak przegrupowanej tabeli możliwe jest wykonywanie funkcji agregacji i dalsza analiza wyników. Przykładowo – chcemy dokonać statystycznego zestawienia imion występujących w tabeli klient: SELECT imię, count(imie) FROM klient GROUP BY imię; Z klauzulą GOUP BY związana jest klauzula HAVING, która działa tak samo jak WHERE dla „normalnej” wersji SELECT: SELECT imię, count(imie) FROM klient GROUP BY imię HAVING imię < 'J'; W ramach laboratorium pokazane zostały metody manipulowania danymi, operatory, zapytania złożone, funkcje agregacji, klauzula GROUP BY oraz możliwość utrwalania zapytań w formie widoków. Na następnych zajęciach wprowadzone zostaną szerzej funkcje i pokazane metody dalszej analizy danych oraz transakcje. Kopie zapasowe i odzyskiwanie danych W PostgreSQL możliwe jest wykonanie kopii zapasowej bazy danych. Można tego dokonać przy pomocy polecenia systemowego pg_dump nazwabazy . W wyniku działania tego polecenia na 16 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski Politechnika Gdańska, międzywydziałowy kierunek „INŻYNIERIA BIOMEDYCZNA” ekranie terminala pojawi się kod SQL , który pozwoli na ponowne założenie bazy danych. Przykład: Opuść bazę danych (\q) pg_dump lab2_login Teraz przekierujmy wynik działania polecenia do pliku: pg_dump lab2_login > lab2_login_dump.sql możesz teraz podejrzeć zawartość tego pliku np. przy pomocy edytora mcedit: mcedit lab2_login_dump.sql W tym miejscu połącz się za swoją bazą ponownie i poćwicz usuwanie danych. Spróbuj skasować płyty, gatunki i wypożyczenia. Teraz możliwe będzie odtworzenie tej bazy danych przy pomocy polecenia psql: Usuńmy bazę danych: dropdb lab2_login stwórzmy ją na nowo (będzie pusta) : createdb lab2_login sprawdźmy, że jest pusta: psql lab2_login \d No relations found \i lab2_login_dump.sql \d SELECT * FROM klient; Widać, że nasza baza odzyskała postać i dane z pliku. 17 BAZY DANYCH, laboratorium nr 2 , A. Bujnowski