Baza podataka Marijan Šuflaj FER, 2018
Sadržaj Baza podataka Upravljanje sadržajem baza CREATE TABLE ALTER TABLE DROP TABLE INSERT SELECT Spajanje tablica UPDATE DELETE
Baza podataka Apstrakcija pohrane podataka i njihovih relacija Direktan pristup datotekama ponekad je problematičan zbog ovlasti Pretraživanje podataka po datotekama značajno je kompliciranije od pretraživanja po bazi podataka Upravitelju zadajemo naredbe u jeziku SQL (Structured Query Language) Jedan upravitelj bazom podataka može imati više baza podataka
Sustav MySQL Besplatni upravitelj bazama podataka Podržava standardne SQL operacije PHP zna komunicirati sa sustavom MySQL docker run -d \ --name oipa-db \ -e MYSQL_ROOT_PASSWORD=oipa-password \ mysql:5.7
Jezik SQL Naredbe se odvajaju pomoću ; Moguće promijeniti Ključne riječi nisu osjetljiva na velika i mala slova, no imena identifikatora jesu Identifikator koji ima prazan znak ili ključnu riječ obaviti u (backtick) Alt Gr + 7 na hrvatskoj tipkovnici Naredbe podjeljene u dvije glavne skupine DDL - data definition language dev.mysql.com/doc/refman/5.7/en/sql-syntax-datadefinition.html DML - data manipulation language dev.mysql.com/doc/refman/5.7/en/sql-syntax-datamanipulation.html
Sustav phpmyadmin Web-sučelje za MySQL bazu podataka docker run --rm \ -d \ --link oipa-db \ -p 8888:80 \ -e PMA_HOST=oipa-db \ -e PMA_USER=root \ -e PMA_PASSWORD=oipa-password \ phpmyadmin/phpmyadmin:4.7 Sučelje dostupno na http://127.0.0.1:8888/
Zadatak 1 Stvorite bazu podataka oipa-predavanja collation neka bude utf8mb4 general ci
Tablica Jedna baza podataka može imati više tablica Tablica ima odredeni broj kolona Može imati proizvoljan broj redaka Svaki stupac ima neki svoj tip podatka koji je isti za sve retke Prvi je stupac najčešće primarni ključ s nekom jedinstvenom vrijednošću za svaki redak
Tipovi podataka Neki najčešći INT - cijeli brojevi FLOAT, DOUBLE - realni brojevi CHAR - niz znakova fiksne duljine Ne bajtova! VARCHAR(n) - niz znakova maksimalne duljine 255 znakova TEXT - niz znakova maksimalne duljine 65535 znakova BLOB - niz bajtova maksimalne duljine 65535 bajtova DATE, TIME, DATETIME - datum, vrijeme, oboje
Vrijednost NULL Predstavlja nepoznatu vrijednost Nije jednak niti jednoj drugoj vrijednosti, a ni sam sebi Usporedba s NULL uvijek rezultira s NULL Koriste se posebni operatori IS NULL i IS NOT NULL Sve kolone podrazumijevaju kako mogu imati NULL vrijednost Onemogućivanje NULL vrijednosti za kolonu pomoću NOT NULL
Stvaranje tablica Pomoću naredbe CREATE TABLE MySQL pretpostavlja InnoDB format CREATE TABLE clanak ( id INT NOT NULL, naslov VARCHAR(256) NOT NULL, sadrzaj MEDIUMTEXT NOT NULL, stvoren DATETIME NOT NULL ); dev.mysql.com/doc/refman/5.7/en/create-table.html
Uredivanje tablica Pomoću ključne riječi ALTER TABLE ALTER TABLE clanak ADD COLUMN status ENUM ('aktivan', 'neaktivan'); dev.mysql.com/doc/refman/5.7/en/alter-table.html
Primarni ključ Identificira jedan redak u tablici Definiciji se kolone nadodaje PRIMARY KEY Implicira kako ne smije biti NULL vrijednosti Ukoliko se ne definira eksplicitno, MySQL definira implicitno Preporuka je da definirate eksplicitno Ubrzava pretraživanje tablice Postavlja ograničenje na jedinstvenu vrijednost Ukoliko pokušamo ubaciti vrijednost koja postoji, dobit ćemo grešku CREATE TABLE bar ( id INT PRIMARY KEY ); ALTER TABLE clanak ADD PRIMARY KEY (id);
Automatski generirane vrijednosti Na prethodni je način naša odgovornost odrediti jedinstvene vrijednosti Moguće je prepustiti tu odgovornost MySQL-u kod cijelih brojeva Koloni se nadodaje AUTO INCREMENT Na svakom ubacivanju retka, MySQL generira novu, strogo monotonu vrijednost CREATE TABLE foo ( id INT PRIMARY KEY AUTO_INCREMENT ); ALTER TABLE clanak CHANGE id id INT AUTO_INCREMENT;
Automatski generirane vrijednosti - razmatranja Olakšavaju ubacivanje u tablice MySQL osigurava jedinstvenost Generirane su vrijednosti strogo monotone Napadaču implicitno pružaju informacije o sustavu Koliko ima stavaka u sustavu Koliko se brzo stvaraju Napadaču olakšavaju navigaciju Zna koje su sljedeća i prethodna vrijednost u nizu Kod resursa dostupnih izvana poželjno je ručno generirati vrijednosti en.wikipedia.org/wiki/universally unique identifier Kod unutarnjih je resursa u redu koristiti automatsku vrijednost Zapisi o pogledima članaka
Uklanjanje tablica Pomoću naredbe DROP TABLE DROP TABLE foo; DROP TABLE bar;
Normalizacija podataka Tablice trebaju biti podijeljene u smislene cjeline Ne želimo duplicirati podatke Nekada ipak želimo? Možemo spojiti više tablica u jedan skup rezultata kod dohvaćanja podataka Kako bismo članku pridjelili kategoriju? Želimo li dodati novu kolonu kategorija?
Tablica kategorija Stvaramo novu tablicu koja predstavlja kategoriju U tablicu clanak dodajemo kolonu kako bismo povezali članak s kategorijom CREATE TABLE kategorija ( id INT PRIMARY KEY AUTO_INCREMENT, ime VARCHAR(256) NOT NULL ); ALTER TABLE clanak ADD COLUMN kategorija_id INT NOT NULL; Promjena se u kategoriji reflektira na sve članke
Strani ključ - engl. foreign key Kolona koju smo stvorili u tablici clanak predstavlja strani ključ Referenca na jedinstveno odredeni redak u povezanoj tablici MySQL može osigurati konzistentnost podataka Neće dopustiti ubacivanje vrijednosti koja ne postoji u povezanoj tablici Obrisati će sve podatke iz trenutne tablice kada se obriše povezani redak u povezanoj tablici Obje kolone moraju imati iste definicije ALTER TABLE clanak ADD CONSTRAINT idx_kategorija_id FOREIGN KEY (kategorija_id) REFERENCES kategorija (id) ON DELETE CASCADE ON UPDATE RESTRICT;
Naredba INSERT Naredba za ubacivanje novih redaka u tablicu Osnovna je forma NSERT INTO tablica (lista stupaca ) VALUES (lista vrijednosti ) INSERT INTO kategorija (ime) VALUE ('PHP'), ('SQL'), ('Programiranje'); dev.mysql.com/doc/refman/5.7/en/insert.html
Naredba SELECT Naredba za dohvaćanje podataka Pojednostavljena je forma SELECT što FROM tablica [WHERE uvjeti ] [LIMIT odmak, broj redaka ] Redoslijed operacija je bitan Znak * predstavlja poseban što koji označava sve kolone SELECT * FROM kategorija; SELECT id, `ime` FROM kategorija; SELECT `ime` FROM kategorija; SELECT k.* FROM kategorija AS k; SELECT k.id FROM kategorija k; dev.mysql.com/doc/refman/5.7/en/select.html
Zadatak 2 Ubacite šest članaka u bazu Tri neka budu u kategoriji PHP Dva neka budu u kategoriji SQL Jedan neka bude u kategoriji Programiranje Jedan neka ima trenutno vrijeme za vrijeme stvaranja Preostalima dajte proizvoljna vremena stvaranja u prošlosti Svako vrijeme neka bude u svom mjesecu
Primjeri # ime kategorije s ID-em 1 SELECT ime FROM kategorija WHERE id = 1; # svi članci iz kategorije s ID-em 1 SELECT id, naslov, stvoren FROM clanak WHERE kategorija_id = 1;
Primjeri - nastavak # dohvati broj kategorija kao polje "c" SELECT COUNT(id) c FROM kategorija; # dohvati kategorije čije je ime dulje # od 3 znaka SELECT ime FROM kategorija WHERE LENGTH(ime) > 3; Funkcija COUNT predstavlja agregacijsku funkciju Više se redaka agregira u jedan
Grupiranje Moguće je grupirati podatke po vrijednostima unutar kolona Definiraju podgrupe podataka nad kojima rade agregacijske funkcije Dohvaćanje vrijednosti kolona koje nisu grupirane nema definirano ponašanje Može biti vrijednost bilo kojeg retka iz podgrupe # kategorije s brojem članaka unutar # te kategorije SELECT kategorija_id, COUNT(id) AS c FROM clanak GROUP BY kategorija_id;
Grupiranje - nastavak Grupiranje se odvija nakon što su retci profiltrirani GROUP BY mora biti nakon WHERE # kategorije s brojem članaka unutar # te kategorije uzevši u obzir samo članke # čiji je naslov dulji od 5 znakova SELECT kategorija_id, COUNT(id) AS c FROM clanak WHERE LENGTH(naslov) > 5 GROUP BY kategorija_id; dev.mysql.com/doc/refman/5.7/en/group-by-functions.html
Filtriranje po agregacijama Pomoću HAVING Filtriranje nakon GROUP BY # kategorije koje imaju više od jednog članka SELECT kategorija_id FROM clanak GROUP BY kategorija_id HAVING COUNT(id) > 1;
Sortiranje Pomoću ORDER BY Bez eksplicitnog sortiranja, poredak redaka ovisi o SQL poslužitelju # članci sortirani po vremenu stvaranja SELECT naslov FROM clanak ORDER BY stvoren; # članci silazno sortirani po naslovu nakon # odredenog datuma SELECT naslov FROM clanak WHERE stvoren >= '2018-03-12 12:23:54' ORDER BY naslov DESC;
Sortiranje - nastavak Moguće je zarezom odvojiti više kolona # članci sortirani po ID-u kategorije # i unutar iste kategorije silazno po naslovu SELECT naslov FROM clanak ORDER BY kategorija_id, naslov DESC; Sortirati se može i po izrazu # članci sortirani po duljini naslova SELECT naslov FROM clanak ORDER BY LENGTH(naslov);
Ograničavanje broja zapisa Pomoću LIMIT # naslov prvog stvorenog članka SELECT naslov FROM clanak ORDER BY stvoren LIMIT 1; Moguće je i preskočiti odredeni broj zapisa # naslov trećeg stvorenog članka SELECT naslov FROM clanak ORDER BY stvoren LIMIT 2, 1; # može i LIMIT 1 OFFSET 2
Kartezijev produkt Rezultat spajanja dvije tablice Redak se svake tablica spaja s retcima druge tablice # kartezijev produkt SELECT * FROM clanak, kategorija;
Kartezijev produkt - nastavak Možemo dodatno filtrirati kako bismo povezali preko stranog ključa # imena članaka s imenima kategorija SELECT c.naslov, k.ime FROM clanak c, kategorija k WHERE c.kategorija_id = k.id;
Spajanje tablica - JOIN Ekspresivnije spajanje tablica Postoje varijacije poput LEFT JOIN i RIGHT JOIN # imena članaka s imenima kategorija SELECT c.naslov, k.ime FROM clanak c JOIN kategorija k ON k.id = c.kategorija_id;
Spajanje tablica - JOIN - nastavak Konceptualno razdvajanje uvjeta spajanja od filtriranja redaka # imena članaka čije je ime dulje od 2 znaka # s imenima kategorija SELECT c.naslov, k.ime FROM clanak c JOIN kategorija k ON k.id = c.kategorija_id WHERE LENGTH(c.naslov) > 2;
Zadatak 3 Stvorite novu tablicu tag koja ima identifikator ime maksimalne duljine 64 znaka Povežite tablice tag i clanak tako da jedan članak može imati nula ili više tagova Ubacite tagove imena OIPA i baza Povežite članke iz kategorije Programiranje s oba taga Povežite članke iz kategorije PHPs tagom OIPA Hint: INSERT INTO... SELECT... JOIN... Kako bismo dohvatili sve članke koji nemaju tag?
Podupiti Jedan je način koristiti podupite Možemo se referencirati na redak za koji se vrši provjera Možete li naslutiti problem u ovom pristuput? SELECT c.naslov FROM clanak c WHERE ( SELECT COUNT(ct.clanak_id) FROM clanak_tag ct WHERE ct.clanak_id = c.id ) = 0;
Vanjska spajanja Spajanje gdje retci ulaze u rezultat makar se ne može spojiti redak iz povezane tablice LEFT JOIN - uvijek se uzimaju retci iz lijeve tablice RIGHT JOIN - uvijek se uzimaju retci iz desne tablice SELECT c.naslov FROM clanak c LEFT JOIN clanak_tag ct ON ct.clanak_id = c.id WHERE ct.clanak_id IS NULL;
Vanjska spajanja - nastavak U kontekstu unutarnjeg spajanja možemo uvjet spajanja staviti i u WHERE bez da se promjeni semantika Možemo li isto napraviti i kod vanjskog spajanja?
Izmjena redaka - naredba UPDATE Pojednostavljena forma: UPDATE tablica SET kolona = vrijednost,... WHERE uvjeti ; # Izmjena imena kategorija iz PHP u "PHP 7.2" UPDATE kategorija SET ime = 'PHP 7.2' WHERE ime = 'PHP'; Oprez! - bez WHERE će svi retci biti izmjenjeni Moguće je staviti i LIMIT dev.mysql.com/doc/refman/5.7/en/update.html
Brisanje redaka - naredba DELETE Pojednostavljena forma: DELETE FROM tablica,... WHERE uvjeti ; # Brisanje članaka objavljenih prije 1.1.2018 DELETE FROM clanak WHERE stvoren < '2018-01-01'; Oprez! - bez WHERE će svi retci biti obrisani Moguće je staviti i LIMIT dev.mysql.com/doc/refman/5.7/en/delete.html
Dodatni resursi Kodne stranice dev.mysql.com/doc/refman/5.7/en/charset-general.html Indeksi dev.mysql.com/doc/refman/5.7/en/column-indexes.html dev.mysql.com/doc/refman/5.7/en/column-indexes.html www.percona.com/blog/2009/06/05/a-rule-of-thumb-forchoosing-column-order-in-indexes/