Manuale Studente Stage Estivo 2007 Database PROGETTAZIONE E UTILIZZO Claudio Bisegni [email protected] http:// www.lnf.infn.it/~bisegni 1 - I Database La gestione dei dati nell’informatica di oggi, è il punto centrale di molte applicazioni pratiche di un software. Gestione Anagrafiche, Conti Bancari, Testi etc. La ricchezza di un patrimonio di informazioni digitale, ha visto negli anni, il crescere e il perfezionarsi di sistemi DBMS(DataBase Management System). Il DBMS è un sistema organizzato di dati che mette a disposizione gli strumenti necessari per accedere alle informazioni in modo semplice ed efficiente. La complessità dei database aumenta negli anni in rapporto alle quantità, sempre più grandi, dei dati da gestire. All’aumento di questi corrisponde una diminuzione dell’efficienza di gestione. Sono state e si svilupperanno quindi tecniche di ottimizzazione delle richieste e di gestione dei dati sempre più perforanti. 2 - Perché usare un database Quando si ha la necessità di gestire molti dati accessibili da più utenze, è d’obbligo l’uso di un database. Nello schema sopra riportato viene mostrata una situazione tipo in cui viene utilizzato un database i cui dati sono letti e scritti da due client e un web server tramite una pagina web con script php. L’utilizzo di un database in questa situazione semplifica molto il lavoro, in quanto a lui verranno demandate la gestione delle situazioni più critiche che questa tipologia di sistemi comporta come ad esempio: accesso concorrente ai dati in scrittura/lettura. Possibilità di lock dei dati in modo che possano essere letti ma non modificati. Tempi ridotti per il prelevamento dei dati a seconda della complessità delle ricerche effettuate. Un database fa tutto questo lavoro per noi, e con l’avanzare delle tecniche di sviluppo software, lo fa ogni giorno che passa nel modo migliore. Per fare un esempio, ogni sito web con contenuti dinamici usa un database, e anche se sottoposto a molte visite riesce a dare un ragionevole tempo di risposta nella fruizione dei dati richiesti. 3 - Didattica del corso Durante il corso verrà realizzato un semplice database di esempio per introdurre le principali caratteristiche del linguaggio SQL. Verranno utilizzate le relazioni (Foreign Key) tra tabelle in modo da creare dei vincoli logici tra i dati, per “proteggerne” inserimento aggiornamento e cancellazione in modo che il database risulti sempre integro nella logica con cui è stato creato. Come motore DBMS verrà utilizzato Mysql, i comandi utilizzati sono simili ai rispettivi degli gli altri database(es: Oracle). Nelle select riportate nel manuale il testo indicato tra parentesi quadri es: [WHERE condizione]. 4 - Ideare un database Per prima cosa occorre pensare a tutti i dati che dobbiamo gestire, nel nostro caso creeremo un database che gestirà i seguenti dati: Alunni Classi Gruppi Culturali (Sport, Scienza, Musica, ecc…). Per dare delle regole al database inseriremo le relazioni tra tabelle che costituiranno la logica con cui i dati fisici sono legati. Il database dovrà rispettare le seguenti regole: Un alunno potrà essere associato ad una sola classe. Un alunno potrà essere associato a nessuno, uno o molti gruppi. 5 - Creazione del Database Per iniziare dobbiamo creare un database di esempio, utilizzeremo il client di MySql autenticandoci con l’utente root per la creazione del Database lnf_demo e dell’Utente lnf. Per eseguire l’amministrazione del database e la creazione dello stesso utilizzeremo il client base di MySql a riga di comando digitando il comando: #> mysql –u root –p ci verrà chiesta l’immissione della password di root e poi avremo accesso al database per creare il DB del nostro progetto. I comandi che utilizzeremo per creare utente e DB sono i seguenti: 1. CREATE DATABASE lnf_demo; 2. CREATE USER `lnf`@`localhost` IDENTIFIED BY 'lnf'; 3. GRANT ALL PRIVILEGES ON lnf_demo . * TO lnf@localhost; Il comando sql (1) crea il database lnf_demo, la seconda crea l’utente. Una considerazione va fatta su come MySql gestisce gli utenti. Nella comando sql (2) avviamo la creazione di un utente lnf che si possa collegare dal localhost (computer si sta lavorando) e di assegnare la password “lnf” a tale utente. Per lo stesso utente si può quindi determinare caratteristiche di accesso differenti a seconda da quale indirizzo si collega. Mysql permette di utilizzare diversi engine per la creazione delle tabelle, per il nostro database utilizzeremo l’engine innodb che permette l’utilizzo delle relazioni e transazioni. 6 - Creazione delle tabelle La tabelle da creare sono quelle di: 1. alunno. 2. classe. 3. gruppo. 4. alunno_gruppo. Immagine 1 Prima di crearle introdurremo il concetto di relazione (Foreign Key – FK1) tra tabelle. Abbiamo detto, nell’introduzione, che nel database i dati sono in relazione tra loro tramite dei legami logici e non fisicamente nelle stesse tabelle. Per descrivere la situazione in cui l’alunno appartiene ad una classe, avremmo potuto mettere l’informazione direttamente nella tabella alunno, cosi facendo avremmo dovuto duplicare le informazioni relative alla classe per ogni alunno. Con l’uso delle relazioni possiamo avere una tabella che descriverà la classe(id, nome, descrizione) e creeremo una relazione tra la tabella alunno e la tabella classe chiamata alunno_classe_idclasse. 1 Relazione che collega un campo di una tabella con il campo di un’altra, in modo che rispetti delle regole. La tabella alunno_gruppo serve per realizzare una relazione del tipo molti a molti: più alunni possono appartenere a più gruppi e le relazioni create sono: alunno_gruppo_idalunno – Relazione con la tabella degli alunni. alunno_gruppo_idgruppo – Relazione con la tabella dei gruppi. Con l’utilizzo delle relazioni tra tabelle le operazioni di Insert, Update e Delete saranno protette dalla possibilità di inserire dati non congruenti, mentre le select supporteranno la le join tra tabelle2. Esempio: il quarto campo della tabella alunno è l’id della classe a cui appartiene, e dallo schema capiamo che uno studente può essere associato ad una sola classe alla volta. La relazione tra i campi alunno.id_classe e classe.id serve ad informare il database che il valore ammissibile per il campo alunno.id_classe deve essere assolutamente un valore presente nel campo id della tabella classe. Se andiamo ad effettuare una Insert nella tabella alunno nel campo id_classe, con un valore non presente nella tabella classe, l’operazione verrà interrotta informandoci della violazione di integrità. Comandi per la creazione delle tabelle: CREATE TABLE `classe` ( CREATE TABLE `alunno` ( `id` INT NOT NULL AUTO_INCREMENT , `id` INT NOT NULL AUTO_INCREMENT , `nome` VARCHAR( 32 ) NOT NULL , `nome` VARCHAR( 32 ) NOT NULL , `descrizione` VARCHAR( 128 ) NOT NULL , `cognome` VARCHAR( 32 ) NOT NULL , PRIMARY KEY ( `id` ) `id_classe` INT NOT NULL, ) TYPE = innodb; PRIMARY KEY (`id`) ) TYPE = innodb; CREATE TABLE `gruppo` ( CREATE TABLE `alunno_gruppo` ( `id` INT NOT NULL AUTO_INCREMENT , `id` int(11) NOT NULL AUTO_INCREMENT, `nome` VARCHAR( 32 ) NOT NULL , `id_alunno` int(11) NOT NULL, `descrizione` VARCHAR( 128 ) NOT NULL , `id_gruppo` int(11) NOT NULL, PRIMARY KEY ( `id` ) PRIMARY KEY (`id`) ) TYPE = innodb; 2 ) ENGINE=innoDB; Tipo di select per visualizzare i dati da più tabelle. Una nota va fatta per i campi id (PrimaryKey - PK3) di ognuna delle tabelle create: il parziale del comando `id` INT NOT NULL AUTO_INCREMENT `id` INT NOT NULL AUTO_INCREMENT indica che il campo id deve essere un intero, non nullo ed e’ di tipo auto incrementanto. L’autoincremento fa si che se il campo corrispondente non è specificato nella Insert, verrà assegnato ad esso il valore successivo al valore più alto già presente nella tabella. Prima di creare le Foreign Key dobbiamo creare un indice per ogni campo di FK con la seguente istruzione SQL ALTER TABLE tabella ADD INDEX (campo) A questo punto possiamo creare le relazioni (Foreign Key – FK) per crearle dobbiamo utilizzare il comando: ALTER TABLE tabella ADD CONSTRAINT nome_foreign_key FOREIGN KEY nome_campo REFERENCES tabella_destinazione (nome_campo) ON DELETE tipo_fk ON UPDATE tipo_fk. Alla ForeignKey per poter operare servono quindi oltre alla tabella e al capo di partenza e di arrivo anche le informazioni su come comportarsi nel caso di un comando UPDATE o DELETE. Questi due comandi saranno descritti più avanti ma introduciamo ora i quattro tipi di constraint associabili alle clausole ON DELETE e ON UPDATE: NO ACTION, (Identica alla RESTRICT). RESTRICT, annulla tutte le operazioni di Update e Delete della tabella_destinazione della FK. CASCADE, dopo un operazione di Delete o Update alla tabella_destinazione applica la Delete o Update ai campi corrispondenti. SET NULL, dopo un operazione di Delete o Update alla tabella_destinazione applica il valore nullo ai campi corrispondenti 3 Chiave che identifica univocamente, nella tabella un record, può essere formata da uno o più campi. I comandi di creazione indice e di creazioni foreign key possono essere concatenati dopo una alter table come nell’esempio successivo: ALTER TABLE tabella ADD INDEX (campo), ADD CONSTRAINT nome_foreign_key FOREIGN KEY nome_campo REFERENCES tabella_destinazione (nome_campo) ON DELETE tipo_fk ON UPDATE tipo_fk. A questo punto possiamo creare le Foreign Key per le tabelle alunno e alunno_gruppo in modo che il database risulti quello in figura 1: ALTER TABLE `alunno` ADD INDEX (`id_classe`), ADD CONSTRAINT `alunno_classe_idclasse` FOREIGN KEY (`id_classe`) REFERENCES `classe` (`id`) ON DELETE RESTRICT; ALTER TABLE `alunno_gruppo` ADD INDEX (`id_alunno`); ALTER TABLE `alunno_gruppo` ADD INDEX (`id_gruppo`); ALTER TABLE `alunno_gruppo` ADD CONSTRAINT `alunno_gruppo_idgruppo` FOREIGN KEY (`id_gruppo`) REFERENCES `gruppo` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, ADD CONSTRAINT `alunno_gruppo_idalunno` FOREIGN KEY (`id_alunno`) REFERENCES `alunno` (`id`) ON DELETE CASCADE ON UPDATE CASCADE; Il database così creato risulta essere quello in figura 1. A questo punto abbiamo un utente(lnf) e un database(lnf_demo) il quale è pronto per ricevere, ricercare, aggiornare e cancellare i dati che dovranno essere gestiti da una nostra ipotetica applicazione. 7 - Inserimento di valori nella tabella Ora passeremo a popolare il database con dei dati di prova. Per questa operazione l’SQL mette a disposizione il seguente comando: INSERT INTO nome_tabella (colonna1,colonna2…colonnaN) VALUES (valore1, valore 2….valoreN) nota: nei comandi di insert, sotto riportati, nei campi non viene riportata colonna dell’id e non ne viene inserito il valore nella sezione dei valori della insert. Questo e’ possibile in quanto abbiamo creato i campi id delle tabelle come auto-increment. In questo modo tali campi saranno riempiti automaticamente dal database al momento della insert. Inserimento dei valori per la tabella delle classi: insert into classe (nome,descrizione) values ('Prima','Prima Classe'); insert into classe (nome,descrizione) values ('Seconda','Seconda Classe'); Inserimento valori per la tabella degli alunni: insert into alunno (nome,cognome,id_classe) values ('Mario','Rossi',1); insert into alunno (nome,cognome,id_classe) values ('Mariolino','Rossi',1); insert into alunno (nome,cognome,id_classe) values ('Luca','Rossini',2); Inserimento valori per la tabella dei gruppi: insert into gruppo (nome,descrizione) values ('Sport','Gruppo Sportivo'); insert into gruppo (nome,descrizione) values ('Musica','Gruppo Musicale'); insert into gruppo (nome,descrizione) values ('Cultura','Gruppo Culturale'); Ora che abbiamo popolato le tre babele principali ci rimane solamente di inserire i valori che associeranno un alunno ad un gruppo: Inserimento valori per la tabella delle associazioni alunni - gruppi: insert into alunno_gruppo (id_alunno, id_gruppo) values (1,1); insert into alunno_gruppo (id_alunno, id_gruppo) values (1,2); insert into alunno_gruppo (id_alunno, id_gruppo) values (2,3); Al primo alunno abbiamo associato due gruppi “Sport” e “Musica” al secondo alunno il solo gruppo “Cultura”. Al terzo alunno non e’ stato associato un gruppo il nostro database non esige l’associazione di un alunno ad un gruppo ma esige l’associazione di un alunno ad una classe. Si noti, che a tale scopo, i valori nella tabella delle classi sono stati i primi ad essere stati inseriti. Se provassimo ad effettuare per prime le insert sulla tabella alunno, riceveremmo un errore di constraint in quanto le classi, associate agli alunni con la FK, non sono ancora state inserite. Questo e’ lo scopo delle Foreign Key, permettono di porre dei vincoli alle operazione di modifica del database, forzano l’integrità strutturale dei dati che il programmatore ha scelto per il suo progetto. 8 - Ricerca dei dati in una tabella Il comando SQL Select permette di visualizzare i valori di una tabella specificando delle condizioni per filtrare il risultato, la sua sintassi è: SELECT [DISTINCT] campo1[,campo2...] FROM tabella AS alias [WHERE condizione_di_ricerca] [ORDER BY condizione_di_ordinamento [ASC | DESC]] Nella select quindi possiamo specificare quali colonne delle tabella interessata vogliamo visualizzare, le condizioni che determinano quali record visualizzare ed infine la condizione tramite la quale il risultato verrà ordinato. Visualizzazione di tutte le colonne di tutti i record della tabella alunni ordinati per nome: select * from alunno order by nome; Visualizzazione del nome e cognome di tutti i record che nel campo cognome contengono ‘Rossi’: select nome, cognome from alunno where cognome = ‘Rossi’; Il simbolo * indica che devono essere usate tutte le colonne della tabella per visualizzare i record. Per le condizioni di ricerca possono essere usate tutte le colonne della tabella. Nella sezione [where] del comando, come gia detto, si possono usare una o tutte le colonne a cui associare dei valori, ma in questo caso come risultato avremo tutti quei record che avranno esattamente lo stesso valore nella stessa colonna. Il problema nasce se si vuole effettuare una ricerca di tutti gli alunni il cui cognome inizia con ‘Ross’. Se nella sezione della where mettessimo where cognome = ‘Ross’ la select non restituirà nessun valore. A questo scopo, per i campi testo, si deve usare la parola chiave like. Questo comando sostituisce il simbolo ‘=’ dopo il nome della colonna e permette di utilizzare il carattere ‘%’ come carattere jolly. Visualizzazione di tutte le colonne di tutti i record della tabella alunno il cui campo ‘cognome’ inizia con i caratteri ‘Ross’: select * from alunno where cognome like 'Ross%'; Nel comando like il carattere jolly ‘%’ viene interpretato come equivalente a n caratteri qualsiasi. Un altro carattere jolly che può essere usato è ‘_’ che equivale a un solo carattere qualsiasi. Visualizzazione di tutte le colonne di tutti i record della tabella alunno il cui campo ‘cognome’ ha qualsiasi stringa formata da cinque caratteri qualsiasi: select * from alunno where cognome like '_____'; 9 - Modifica dei dati in una tabella Per effettuare una modifica dei dati l’sql mette a disposizione il comando update, sotto riportato nella sua versione semplificata: UPDATE nome_tabella SET colonna1 = valore1 [, [WHERE condizione] colonna2 = valore2 ...] la condizione serve a filtrare i record che devono essere aggiornati, omettendo la condizione where, la modifica viene eseguita su tutti i record della tabella. Nel paragrafo precedente, abbiamo associato all’alunno Mario Rossi alla prima classe, quando si avrà necessita di registrare la promozione, la classe a lui associata dovrà cambiare e dovrà essere assegnato alla seconda classe ID_CLASSE = 2 il comando sotto riportato effettua questa modifica. Essendo Mario il primo alunno inserito il suo ID(Primary Key – auto increment) sarà ID=1, e per sincerarci di questa cosa faremo la seguente select: SELECT id FROM alunno WHERE nome = ‘Mario’ AND cognome = ‘Rossi’ Se dovesse essere diverso dovrà essere usato l’id riportato dalla select Modifica della classe associata all’alunno Mario Rossi: update alunno set id_classe = 2 where id = 1; 10 - Ricerca dei dati tra più tabelle I comandi che abbiamo visto fino ad ora lavorano su una tabella alla volta. Il comando Select permette di fare ricerche da più tabelle contemporaneamente. Questo può avvenire facendo uso delle foreign key: Lista di tutti gli alunni con in più il la descrizione della classe di appartenenza: select a.nome, a.cognome, c.descrizione from alunno a, classe c where a.id_classe = c.id; L’esempio sopra riportato produrrà il seguente output: +-----------+---------+----------------+ | nome | cognome | descrizione | +-----------+---------+----------------+ | Mario | Rossi | Prima Classe | | Mariolino | Rossi | Prima Classe | | Luca | Rossini | Seconda Classe | La differenza delle select viste fin ora sta nel fatto che in quella dell’esempio viene inserita una seconda tabella, quella della classe. I caratteri ‘a’ e ‘c’ sono usati come alias per la tabella senza i quali avremmo dovuto usare, nella sezione dei campi della select, i nomi della relativa tabella per ognuno di essi. Tra le tabelle alunno e classe esiste una foreign key ed e’ quella che collega l’alunno(id_classe) alla classe(id). quindi nella sezione della ‘where’ della select specifichiamo la condizione in modo da informare il database come collegare le due tabelle. Nel nostro caso a.id_classe = c.id; Vogliamo ora visualizzare tutti gli alunni appartenenti alla prima classe, la select diventerà: Lista di tutti gli alunni con in più il la descrizione della classe di appartenenza: select a.nome, a.cognome, c.descrizione from alunno a, classe c where a.id_classe = c.id and c.id = 1; L’esempio ci mostra come oltre alle condizioni di join possono essere usate altre condizioni che soddisfano i criteri di ricerca desiderati. Vogliamo ora visualizzare i gruppi associati ad ogni alunno. In questo caso le tabelle da prendere in considerazione sono tre: alunno, alunno_gruppo, gruppo. Lista degli alunni con la descrizione del gruppo di appartenenza: select a.nome, a.cognome, g.descrizione from alunno a, alunno_gruppo ag, gruppo g where a.id = ag.id_alunno and ag.id_gruppo = g.id; +-----------+---------+------------------+ | nome | cognome | descrizione | +-----------+---------+------------------+ | Mariolino | Rossi | Gruppo Culturale | | Mario | Rossi | Gruppo Musicale | | Mario | Rossi | Gruppo Sportivo | +-----------+---------+------------------+ Come si vede, nel risultato della select manca l’unico alunno ‘Luca Rossini’ il quale non e’ associato a nessun gruppo. Può essere necessario, a volte, avere tutti i record della tabella di partenza anche se non c’è una associazione tra i valori delle tabelle in join si parla in questo caso di left join. La situazione sopra descritta, riferita al nostro caso, coincide con l’esigenza di avere tutti i nomi degli alunni, e per quelli non associati ad un gruppo. La select che realizza questa situazione è: Lista di tutti gli alunni con in più il la descrizione della classe di appartenenza: select a.nome, a.cognome, g.descrizione from alunno a left join (alunno_gruppo ag, gruppo g) on a.id = ag.id_alunno and ag.id_gruppo = g.id; L’output sarà: +-----------+---------+------------------+ | nome | cognome | descrizione | +-----------+---------+------------------+ | Mario | Rossi | Gruppo Musicale | | Mario | Rossi | Gruppo Sportivo | | Mariolino | Rossi | Gruppo Culturale | | Luca | Rossini | NULL | +-----------+---------+------------------+ Come vediamo la left join forza la selezione di tutti i record contenuti nella tabella alunno e per quelli in relazione col gruppo ne mostra il nome , mentre per l’alunno associato a nessun gruppo mostra il valore NULL. 11 - Cancellazione dei dati nella tabella Le operazioni viste fino ad ora permettono l’inserimento, la modifica e la ricerca dei dati inseriti in un database. Rimane infine un ultima operazione, la cancellazione. DELETE FROM nome_tabella [WHERE condizione] Il comando necessita due informazioni per poter essere eseguito, il nome della tabella e la condizione con cui effettuare la cancellazione: Cancellazione di Mariolino Rossi dalla tabella ‘alunno’; delete from alunno where nome='Mariolino'; Con il comando nell’esempio viene cancellato l’alunno Mariolino dalla tabella ‘alunno’. La tabella alunno_gruppo ha una foreign key in cascade verso la tabella alunno e la tabella gruppo. Quindi ci aspettiamo che la relazione con l’alunno ‘Mariolino’ sia sparita automaticamente dalla tabella ‘alunno_gruppo’. Visualizzazione del contenuto della tabella alunno_gruppo. select * from alunno_gruppo; L’output sarà: +----+-----------+-----------+ | id | id_alunno | id_gruppo | +----+-----------+-----------+ | 1 | 1 | 1 | | 2 | 1 | 2 | +----+-----------+-----------+ si vede quindi che la relazione e’ stata rispettata e il DBMS ha cancellato il record dove ’id_alunno’ coincideva con il record relazionato e cancellato nella tabella ‘alunno’. 12 – Transazioni Fino ad ora Mysql e’ stato usato in modalità ‘Auto Commit’. Ogni volta che viene eseguito un comando di inserimento modifica o cancellazione, il database applica l’operazione rendendola disponibile a qualsiasi sessione attiva sul database. Ogni connessione apre una sessione di lavoro, quindi ogni comando impartito produrrà degli effetti che saranno visti anche da altre connessioni. Possono verificarsi delle situazioni in cui, prima di rendere effettive le modifiche apportate, vanno modificate altre tabelle per poter rendere i dati integri. Altro scenario possibile e’ quello in cui più pagine web debbano inserire dati nella stessa tabella e alla fine l’utente ha la possibilità di salvare o annullare l’inserimento. In entrambe le situazioni descritte abbiamo bisogno delle Transazioni. Di default mysql non abilita le transazioni quindi per poterle attivare, dobbiamo usare il comando: Disabilitazione AUTO COMMIT set autocommit 0 Abilitazione AUTO COMMIT set autocommit 1 Disattivando la funzione AUTO COMMIT ogni comando inizia una transazione che verrà terminata dai comandi: ROLLBACK – Annulla la transazione in corso COMMIT – Fissa i valori inseriti modificati o cancellati, rendendoli disponibili alle altre connessioni. Quando una transazione viene cominciata in una sessione, le altre non vedono le modiche effettuate fino a che il comando commit le fissa e le rende disponibili. Quindi i comandi sopra descritti vengono utilizzati per annullare o salvare le modifiche apportate a tutto il database.