Imię i Nazwisko Data Ocena Laboratorium 7 Celem tego ćwiczenia jest pokazanie, że w MoscowML można pisać aplikacje użytkowe, np. prosty interpreter języka SQL (MLSQL) Listy i krotki Różnica pomiędzy krotkami a listami. (2,10) dwuelementowa krotka (para) (3,5,9) trójelementowa krotka [2,10] lista składająca się z dwóch elementów [1,3,5,7,9] lista składająca się z pięciu elementów [(1,2,3),(4,5,6)] dwuelementowa lista trójelementowych krotek ([1,2],[3,4],[5,6]) trójelementowa krotka dwuelementowych list Podsumowując: do definiowania list używamy nawiasów kwadratowych [ ], a do definiowania krotek używamy nawiasów okrągłych ( ). Czym jest baza danych? Baza danych to zbiór danych zapisanych w ściśle określony sposób w strukturach odpowiadających założonemu modelowi danych. W potocznym ujęciu obejmuje dane oraz program komputerowy wyspecjalizowany do gromadzenia i przetwarzania tych danych. Program taki (często zestaw programów) nazywany jest "Systemem zarządzania bazą danych" (ang. DataBase Management System, DBMS). W ścisłej nomenklaturze baza danych oznacza zbiór danych, który zarządzany jest przez system DBMS. W tym ćwiczeniu Systemem zarządzania bazą danych jest program napisany w MoscowML, natomiast bazą danych, na której będą wykonywane ćwiczenia jest baza zawierająca informacje o studentach. Baza danych składa się z tabel. Tabelę można (w pewnym uproszczeniu) porównać do dwuwymiarowej tablicy przechowującej jakieś dane. Poniżej przedstawiam przykładową tabelę 1
Studenci imie nazwisko nr_albumu wiek semestr Tomasz Nowak FD-104415 22 5 Tomasz Kowalski FD-104255 21 3 Piotr Górka BD-104623 21 3 Anna Skoczylas PDF-105623 20 1 Powyższa tabela składa się z 5 kolumn (imie, nazwisko, nr_albumu, wiek, semestr) i 4 rekordów. Rekord to jeden wiersz tabeli zawierający komplet informacji o danym studencie. Struktura bazy danych się nieznacznie komplikuje, gdyż niektóre informacje (np. nazwisko) są przechowywane w bazie danych jako string (varchar), a niektóre jako int (np. wiek). Język MoscowML nie obsługuje tablic znanych z innych języków programowania, więc wszystkie informacje o studentach są zapisywane w listach. Poniższy fragment kodu przechowuje dane studentów, które są podane w powyższej tabeli datatype stype = sint of int sstring of string; val kolstudenci = [("imie", "varchar"), ("nazwisko", "varchar"), ("nr_albumu", "varchar"), ("wiek", "int"), ("semestr", "int")]; val rekstudenci = [[sstring "Tomasz", sstring "Nowak", sstring "FD-104415", sint 22, sint 5], [sstring "Tomasz", sstring "Kowalski", sstring "FD-104255", sint 21, sint 3], [sstring "Piotr", sstring "Górka", sstring "BD-104623", sint 21, sint 3], [sstring "Anna", sstring "Skoczylas", sstring "PDF-105623", sint 20, sint 1]]; val baza = ref [("Studenci", kolstudenci, rekstudenci)]; Lista o nazwie kolstudenci zawiera informacje o strukturze tabeli studenci. Elementami tej listy są pary (krotki) zawierające informacje o nazwie kolumny i jej typie. Lista o nazwie rekstudenci zawiera informacje o studentach. Elementami tej listy są listy. Listę list można porównać do dwuwymiarowej tablicy lub do tablicy rekordów. Lista o nazwie baza składa się z trójelementowych krotek. pierwszy element krotki to string zawierający nazwę tabeli, drugi element krotki jest listą krotek zawierającą informacje o kolumnach owej tabeli trzeci element krotki jest listą list zawierającą dane wszystkich studentów 2
Wyświetlanie danych zawartych w bazie danych Uwaga: najwygodniej będzie przeprowadzać wszystkie ćwiczenia w Trybie interaktywnym (zobacz poniższy rysunek) W języku SQL, aby pobrać wszystkich studentów należy wpisać następującą komendę: SELECT * FROM Studenci; Odpowiedź SQL'a: +--------+-----------+------------+------+---------+ imie nazwisko nr_albumu wiek semestr +--------+-----------+------------+------+---------+ Tomasz Nowak FD-104415 22 5 Tomasz Kowalski FD-104255 21 3 Piotr Gˇrka BD-104623 21 3 Anna Skoczylas PDF-105623 20 1 +--------+-----------+------------+------+---------+ 4 rows in set (0.00 sec) W interpreterze SQL'a napisanym w ML'u składnia jest nieco inna: SELECT "* FROM Studenci"; Odpowiedź MLSQL'a imie nazwisko nr_albumu wiek semestr ------------ ------------ ------------ ------------ ------------ Tomasz Nowak FD-104415 22 5 Tomasz Kowalski FD-104255 21 3 Piotr Gˇrka BD-104623 21 3 Anna Skoczylas PDF-105623 20 1 4 row(s) in set 3
Aby pobrać z bazy danych studentów, którzy mają 22 lata w SQLu użyć następującego polecenia: SELECT * FROM Studenci WHERE wiek=22; Odpowiedź SQL'a: +--------+----------+------------+------+---------+ imie nazwisko nr_albumu wiek semestr +--------+----------+------------+------+---------+ Tomasz Nowak FD-104415 22 5 +--------+----------+------------+------+---------+ 1 row in set (0.02 sec) W MLSQL'u składnia jest nieco inna: SELECT "* FROM Studenci WHERE wiek=22"; Odpowiedź MLSQL'a: imie nazwisko nr_albumu wiek semestr ------------ ------------ ------------ ------------ ------------ Tomasz Nowak FD-104415 22 5 1 row(s) in set Dodawanie nowych rekordów Załóżmy, że nowy student w środku semestru został przeniesiony z innej uczelni na Politechnikę Rzeszowską. W tym celu należy w SQLu wpisać następującą komendę: INSERT INTO Studenci VALUES ('Przemek', 'Kowalski', 'MDT-101226',21,5); Odpowiedź SQL'a Query OK, 1 row affected (0.05 sec) w MLSQL'u składnia polecenia jest podobna: INSERT "INTO Studenci VALUES ('Przemek', 'Kowalski', 'MDT-101226', 21, 5)"; 4
Odpowiedź MLSQL'a Query OK, 1 row affected (0.01 sec) Ćwiczenie: Za pomocą poznanej wcześniej instrukcji SELECT sprawdź, czy dane nowego studenta rzeczywiście są zapisane w bazie danych. Dokładniejsze omówienie funkcji INSERT Jak to się dzieje, że po wpisaniu komendy INSERT "INTO Studenci VALUES ('Przemek', 'Kowalski', 'MDT-101226', 21, 5)"; do bazy zostanie dodany nowy wiersz? Otóż zostaje wywołana funkcja o nazwie INSERT z parametrem typu string o zawartości INTO Studenci VALUES ('Przemek', 'Kowalski', 'MDT-101226', 21, 5) Funkcja INSERT fun INSERT s = let val lista = String.tokens (fn x => x = #" " orelse x = #"(" orelse x = #")" orelse x = #"," orelse x = #"'") s; val tabela = if List.nth(lista, 0) <> "INTO" then raise BLAD_SKLADNI else List.nth(lista,1); in end; val wiersz = popargs(lista, 3); val _ = baza := insertinto(!baza, tabela, wiersz); print "Query OK, 1 row affected (0.01 sec) \n" Funkcja string.tokens spowoduje rozbicie długiego stringa na listę stringów. Separatorami są takie znaki jak: spacja nawiasy okrągłe przecinek apostrof Przykładowo: gdy do funkcji wchodzi string INTO Studenci VALUES ('Przemek', 'Kowalski', 'MDT-101226', 21, 5) to zostanie on rozbity na następującą listę stringów: ["INTO", "Studenci", "VALUES", "Przemek", "Kowalski", "MDT-101226", "21", "5"] Następnie należy sprawdzić, czy pierwszy argument listy zawiera ciąg znaków INTO (w tym celu korzystamy z bibliotecznej funkcji List.nth). Jeśli jest to inny ciąg znaków, to zostanie wygenerowany wyjątek BLAD_SKLADNI (wyjątek powoduje przerwanie wykonywania funkcji INSERT). 5
Kolejnym etapem jest zapisanie do nowej listy o nazwie wiersz listy lista okrojonej o 3 pierwsze elementy (czyli lista wiersz zawiera następujące elementy: "Przemek", "Kowalski", "MDT- 101226", "21", "5"). Za usunięcie n pierwszych elementów listy odpowiada funkcja popargs Przedostatnim elementem jest wywołanie funkcji insertinto, która zajmuje się dodaniem wartości (znajdujących się w liście wiersz) do listy o nazwie baza, której budowa została dokładnie omówiona na początku instrukcji. Na samym końcu (za pomocą instrukcji print)zostaje wyświetlony komunikat o dodaniu wiersza do bazy danych. Edycja rekordów Podczas wprowadzania danych studentów nastąpiła pomyłka. Należy koniecznie je skorygować. Oto prosty przykład modyfikowania danych w SQLu: UPDATE Studenci SET wiek=19 WHERE semestr=1; (wszyscy studenci którzy mają 19 lat zostaną automatycznie przepisani na pierwszy semestr) Odpowiedź SQLa: Query OK, 1 row affected (0.03 sec) Rows matched: 1 Changed: 1 Warnings: 0 w MLSQL'u składnia polecenia jest podobna: UPDATE "Studenci SET wiek=19 WHERE semestr=1"; Odpowiedź MLSQL'a: Query OK, 1 row(s) affected (0.01 sec) Ćwiczenia: 1) Za pomocą poznanej wcześniej instrukcji SELECT sprawdź, czy dane zostały zmienione 2) Ponieważ Tomasz Kowalski kolejny raz nie zaliczył analizy matematycznej został degradowany na 1 semestr. Podaj odpowiednią komendę, która dokona tej zmiany. Wskazówka: zauważ, że w bazie danych jest dwóch Tomków i dwóch Kowalskich. Dlatego w celu zidentyfikowania studenta należy posłużyć się numerem indeksu. 6
Kasowanie rekordów W SQL'u należy wpisać następującą komendę: DELETE FROM Studenci WHERE nr_albumu='fd-104415'; Odpowiedź SQL'a: Query OK, 1 row affected (0.11 sec) w MLSQL'u składnia polecenia jest podobna: DELETE "FROM Studenci WHERE nr_albumu='fd-104415'"; Odpowiedź MLSQL'a: Query OK, 1 row(s) affected (0.01 sec) Ćwiczenie: Sprawdź, czy na pewno dane usuniętego studenta nie figurują w bazie danych. Tworzenie nowych tabel W bazie danych oprócz studentów będą zapisywane osoby, które otrzymują stypendia. W tym celu należy utworzyć nową tabelę, która będzie zawierała dwie kolumny: nr_albumu i wysokość stypendium. Aby wykonać to zadanie, należy w SQL'u wpisać następującą komendę CREATE TABLE stypendium (numer_albumu varchar(10), kwota int); Odpowiedź SQL'a: Query OK, 0 rows affected (0.39 sec) W MLSQL'u składnia jest podobna: CREATE "TABLE stypendium (numer_albumu varchar, kwota int)"; Odpowiedź MLSQL'a: Query OK, 0 rows affected (0.01 sec) 7
Ćwiczenia: 1) Za pomocą poznanej instrukcji SELECT sprawdź, czy w nowej tabeli są zapisane jakieś rekordy 2) Dodaj do tabeli dwa przykładowe rekordy 3) Za pomocą poznanej instrukcji SELECT sprawdź, czy coś się zmieniło Usuwanie wszystkich rekordów z tabeli Komisja stypendialna stwierdziła, że uczelnia nie spełnia najnowszych norm ISO, więc nie ma prawa wypłacać studentom stypendiów. Usuń z tabeli stypendia wszystkie rekordy. Aby usunąć wszystkie rekordy można użyć polecenia: TRUNCATE TABLE stypendia; Odpowiedź SQL'a Query OK, 2 rows affected (0.11 sec) W MLSQL'u składna jest bardzo podobna: TRUNCATE "TABLE stypendia"; Odpowiedź MLSQL'a > val it = "OK" : string INNY SPOSÓB w SQL'u można napisać: DELETE FROM stypendia; (jednak polecenie TRUNCATE TABLE w większości przypadków jest dużo szybsze) w MLSQL'u można napisać: DELETE "FROM stypendia"; Ćwiczenie: Wyobraź sobie sytuację, że uczelnia została zamknięta. Usuń wszystkich studentów. I tym optymistycznym akcentem kończymy zajęcia ;-) 8
Podsumowanie W SQLu W ML'u SELECT * FROM tabela; lub SELECT * FROM tabela WHERE warunek; INSERT INTO tabela VALUES (wartości); UPDATE tabela SET kolumna=wartość WHERE warunek; DELETE FROM tabela WHERE warunek; CREATE TABLE nazwa_tabeli (kolumna1 typ1, kolumna2 typ2...); Pobieranie danych z BD Dodawanie rekordów do BD Aktualizacja rekordów Usuwanie rekordów Tworzenie nowej tabeli SELECT "* FROM tabela"; lub SELECT "* FROM tabela WHERE warunek"; INSERT "INTO tabela VALUES (wartości)"; UPDATE "tabela SET kolumna=wartość WHERE warunek"; DELETE "FROM tabela WHERE warunek"; CREATE "TABLE nazwa_tabeli (kolumna1 typ1, kolumna2 typ2...)"; TRUNCATE TABLE tabela; lub DELETE FROM tabela; Usuwanie wszystkich wierszy z tabeli TRUNCATE "TABLE tabela"; lub DELETE "FROM tabela"; 9