Visualizzazione post con etichetta MySQL. Mostra tutti i post
Visualizzazione post con etichetta MySQL. Mostra tutti i post

giovedì 30 settembre 2010

DBConnector

DBConnector è un client per database, basato sulle API JDB, viste in un precedente articolo. Lo sviluppo del client è stato portato avanti attraverso NetBeans IDE, che favorisce e semplifica di molto il disegno delle GUI! Per la progettazione ho cercato invece di seguire il noto schema MVC (model-view-controller). Potete scaricare a questo indirizzo l'intera cartella contenente i file di progetto, oltre al file jar che esegue l'applicazione stessa. Il file in questione è un archivio tar che va dunque estratto con il comando: tar zxvf DBConnector.tar.gz, da eseguire all'interno della cartella che ospita il file appena scaricato. L'estrazione produce la cartella DBConnector, al suo interno trovate il codice sorgente (cartella /src), la documentazione generata con il comando javadoc (cartella /dist/javadoc) e il file jar per eseguire l'applicazione (nella cartella dist). Quest'ultima avviene digitando il comando: java -jar DBConnector.jar.

Il programma si compone di cinque classi, una dedicata all'avvio dell'applicazione (DBConnectorApp). Una classe descrive l'interfaccia grafica (DBGui), altre due si preoccupano della connessione (DBConnector) e dell'invio dei comandi SQL al database (DBQuest). L'ultima classe è quindi dedicata alla gestione degli eventi (DBController), raccoglie i comandi SQL e li fa eseguire all'oggetto incaricato, quindi aggiorna l'interfaccia grafica riportando errori e risultati. Nell'esempio viene mostrata l'applicazione in esecuzione, il driver usato nell'esempio permette la connessione a un database MySQL che ho progettato in precedenza per un'applicazione web.

giovedì 23 settembre 2010

JDBC, esempio

State scrivendo un'applicazione che usa un driver JDBC? Ecco i passi da seguire:

  1. Caricare il driver, specifico per il database in uso;
  2. Creare, mediante url, una connessione al database da usare;
  3. Scrivere e inviare il comando SQL alla connessione;
  4. Scorrere il risultato alla ricerca dei valori utili;
  5. Chiudere la connessione al database;

Per caricare il driver occorre specificare al metodo Class.forName(driver).newInstance() una stringa che identifica il nome del driver.  Ad esempio, la stringa che identifica il driver JDBC per MySQL è com.mysql.jdbc.Driver. Non dimenticate di invocare newIstance(), dopo aver cercato il driver, per creare una nuova istanza del driver! Altre stringhe di driver sono: sun.jdbc.odbc.JdbcOdbcDriver per un bridge (ponte) JDBC-ODBC, oracle.jdbc.driver.OracleDriver per Oracle e org.postgresql.Driver per PostgreSQL.

Anche l'url da usare per la connessione va cercato nella dicumentazione. MySQL e altri database sono sviluppati sul modello client/server. Il server che ospita dati, sia locale che remoto, è quindi identificato da un indirizzo di rete. Per MySQL questo url ha la seguente struttura: jdbc:mysql://indirizzo_host:porta/nomeDataBase. Se il server MySQL è dunque locale occorre indicare come host l'indirizzo localhost oppure il noto ip di loopback 127.0.0.1. Il numero di porta usato di default dal server MySQL è 3306 (se tale numero non è stato modificato dopo l'installazione del server). Con DriverManager.getConnection(url,user,password) viene tentata una connessione al database (specificato nell'url), se il database richiesto non è pubblico occorre allora indicare il nome utente e la password usate in fase di login (come quando usiamo il client MySQL dal terminale).

Terminata la fase di setup per la connessione si possono iniziare a utilizzare i metodi visti in precedenza per l'invio dei comandi SQL. Terminata l'elaborazione chiudete la connessione con il metodo connessione.close().


import java.sql.*;



public class Main {
 public static void main(String[] args) {
  String driver="com.mysql.jdbc.Driver";
  String user="root";
  String password="root";
  String url="jdbc:mysql://localhost:3306/php_training";
  String query="SELECT * FROM contatti WHERE nome='Luca'";
  try {
   System.out.println("Caricamento del driver ("+driver+")...");
   Class.forName(driver).newInstance();
   Connection connessione=DriverManager.getConnection(url,user,password);
   if(!connessione.isClosed()) System.out.println("Connessione...");
   else {
    System.out.println("Connessione non riuscita!");
    System.exit(1);
   }
   Statement stmt=connessione.createStatement();
   System.out.println("Esecuzione della query: "+query);
   System.out.println("Risultato:");
   ResultSet srs = stmt.executeQuery(query);
   while (srs.next()) {
    System.out.println(srs.getString("nome")+" "+srs.getString("cognome"));
   }
   connessione.close();
  }
  catch(Exception errore) {
   System.err.println("Si è verificata un'eccezione: " + errore.getMessage());
  }
 }
}

Ecco cosa riporta la finestra di output in NetBeans IDE (ho usato il database php_training):

mercoledì 22 settembre 2010

JDBC: Java DataBase Connectivity, introduzione

Java DataBase Connectivity, da ora in avanti JDBC, è una libreria Java che implementa i driver per l'accesso e la gestione dei database nelle applicazioni Java.vEsistono diverse implementazioni dei driver JDBC, che possiamo classificare così come segue:

  • Driver di tipo 1: sono driver che implementano le API JDBC facendo corrispondere le chiamate ad altre API per l'accesso ai dati. Effettuano cioè un ponte (bridge) verso altre tipologie di driver, traducendo quindi ogni chiamata in chiamate dell'altro driver. Il driver JDBC-ODBC è un esempio di driver di tipo 1;
  • Driver di tipo 2: sono driver che, essendo scritti parzialmente in Java e parzialmente con il codice nativo del database, traducono ogni chiamata in una chiamata interna al database;
  • Driver di tipo 3: sono driver scritti interamente in Java e comunicano con un server middleware usando un protocollo indipendente dal database. Il server comunica, poi, le richieste del client all'origine dei dati;
  • Driver di tipo 4: sono driver scritti interamente in Java che si connettono direttamente all'origine dei dati, senza server intermedio;

I driver di tipo 1 e 2, poiché scritti con codice nativo, possono limitare l'applicazione Java in termini di portabilità. L'impiego di questi driver implica di conseguenza l'installazione delle librerie di supporto usate nel codice nativo del driver. Ad esempio, il driver di tipo 1 JDBC-ODBC dipende anche dal driver ODBC. Il driver di tipo 2 è da preferire a quello di tipo di 1 (ogni chiamata è tradotta in una chiamata interna al database). In ogni caso, se il produttore di database ne ha previsto uno, è preferibile usare un driver scritto interamente in Java.

I driver di tipo 3 e 4 hanno in genere prestazioni migliori, per i motivi scritti sopra. La portabilità dell'applicazione è quindi maggiore (non dovendo installare altro software). I driver di tipo 4 usano un protocollo nativo per scambiare i dati con il database, se possibile si consiglia di usare sempre driver di tipo 4. I driver di tipo 3 usano invece un protocollo di rete per dialogare con un server che interagisce con il database per conto del client. E' proprio quest'ulteriore passaggio a degradare le prestazioni del driver!

L'uso di JDBC prevede in primo luogo la creazione di una connessione verso il database, che può avvenire seguendo uno dei due meccanismi di seguito elencati:

  • DriverManager: carica un driver specifico utilizzando un indirizzo registrato che sintetizza la connessione al database;
  • DataSource: da preferire al precedente meccanismo poiché offre maggiori informazioni sui dati;

Per stabilire una connession sono necessari due passaggi: caricare un driver ed eseguire la connessione (usando il driver). Il caricamento di un driver si risolve in una riga di codice:

Class.forName("NOME_DEL_DRIVER");

Il nome del driver viene indicato nella documentazione del driver. La chiamata a Class.forName realizza automaticamente un'istanza  del driver registrandola con DriverManager. Solo dopo aver caricato un driver si può tentare di aprire una connessione verso il database desiderato. Vediamo come funzionano i due meccanismi.

La classe DriverManager fa parte dell'interfaccia Driver e realizza pertanto i metodi richiesti dalla stessa. Il metodo più usato è getConnection che ritorna un oggetto Connection. Esistono più implementazioni di questo metodo, che ovviamente  differiscono per la firma e quindi dai parametri richiesti dal metodo. La forma base è la seguente:

DriverManager.getConnection(String url);
dove url è l'url al database (nella forma jdbc:subprotocol:subname). Altre varianti di getConnection permettono poi di specificare proprietà, come:
DriverManager.getConnection(String url,Properties info);
dove info rappresenta un set di proprietà per la connessione. In molti casi va specificata la password e il nome utente assegnati allo stesso sul database. Il metodo:
DriverManager.getConnection(String url,String user,String password);
permette di fare proprio questo! In caso di errore tutti e tre i metodi appena visti lanciano un'eccezione di tipo SQLException. Altri metodi utili di DriverManager sono setLoginTimeout(int x) che attende x secondi prima di eseguire il login, setLogWriter(PrintWriter out) che imposta out come standard output per gli eventi di logging e println(String messaggio) che stampa su out un evento utile all'operazione di logging.
Con DataSource anziché fornire informazioni specifiche sul driver è sufficiente fornire un nome logico che punta alla sorgente di dati. Ecco un esempio:
InitialContext ic=new InitialContext();
DataSource ds=ic.lookup("java:comp/env/jdbc/myDB");
Connection con=ds.getConnection();
DataSource ds=(DataSource)org.apache.derby.jdbc.ClientDataSource();
ds.setPort(1527);
ds.setHost("localhost");
ds.setUser("APP");
ds.setPassword("APP");
Connection con=ds.getConnection();

L'interfaccia ResultSet fornisce i metodi per recuperare e manipolare i risultati generati dalle query eseguite. Gli oggetti ResultSet (una struttura dati in grado di ospitare i dati generati da una query) possono avere diverse funzionalità e caratteristiche. Tali caratteristiche possono stabilire il tipo di dati, la concorrenza e i cursori per lo scorrimento dei risultati. Il tipo di un oggetto ResultSet stabilisce i modi in cui il cursore può essere manipolato e le modifiche concorrenti che possono essere apportate ai dati. Esistono tre tipologie di ResultSet:

  • TYPE_FORWARD_ONLY: non è possibile scorrere a piacimento il risultato ottenuto (eseguendo una query), il cursore si sposta sempre e solo in avanti, dalla prima all'ultima riga;
  • TYPE_SCROLL_INSENSITIVE: è possibile scorrere il risultato in entrambe le direzioni oppure usare scostamenti relativi per l'accesso a una riga del risultato;
  • TYPE_SCROLL_SENSITIVE: è possibile scorrere il risultato in entrambe le direzioni oppure usare scostamenti relativi per l'accesso a una riga del risultato. La differenza con l'altro tipo di cursore risiede nella possibilità di rendere visibili i cambiamenti nei dati ad una applicazione;
JDBC restituisce i risultati in un oggetto ResultSet, quindi la nostra applicazione deve necessariamente dichiarare un'istanza di tale classe per raccogliere i risultati. Tale oggetto viene restituito ad esempio quando eseguiamo una chiamata a executeQuery o getResultSet. I metodi appena citati appartengono alla classe Statement, che permette di definire oggetti per l'esecuzione di codice SQL (il cui risultato è quindi ospitato in un oggetto ResultSet).
Statement stmt=con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
ResultSet srs = stmt.executeQuery("SELECT * FROM contatti");
Il primo argomento è una delle tre costanti definite in ResultSet per indicare il tipo di cursore: TYPE_FORWARD_ONLY, TYPE_SCROLL_INSENSITIVE e TYPE_SCROLL_SENSITIVE. Il secondo argomento specifica (con due costanti definite sempre in ResultSet) se un set di risultati è di sola lettura oppure aggiornabile: CONCUR_READ_ONLY e CONCUR_UPDATABLE. Un altro metodo della classe Statement è int executeUpdate(String sql) che esegue codice SQL che non produce oggetti ResultSet (come operazioni di INSERT, UPDATE o DELETE). Ricorda che se specifichi il tipo di cursore devi sempre precisare, poi, se questo è di sola lettura oppure aggiornabile. L'istanza di un semplice oggetto Statement, con createStatement, invece, dichiarerà di default un  oggetto ResultSet di tipo TYPE_FORWARD_ONLY e accessibile nella modalità CONCUR_READ_ONLY.
Avete notato la variabile srs nel codice precedente? Si tratta di un oggetto ResultSet che racchiude al suo interno il risultato della query! Tale oggetto può essere manipolato attraverso svariati metodi. L'istanza di tale oggetto posiziona di default un puntatore per lo scorrimento della tabella (il risultato di un query è in ogni caso una tabella) sulla prima riga del risultato. Particolarmente utili sono i metodi:
  • next(): sposta il cursore sulla riga successiva a quella puntata, se esiste;
  • previous(): sposta il cursore sulla riga precedente a quella puntata, se esiste;
  • first(): sposta il cursore sulla prima riga dell'oggetto ResultSet;
  • last: sposta il cursore sull'ultima riga dell'oggetto ResultSet;
  • relative(int n): sposta il cursore di n righe (in avanti o dietro, dipende dal segno di n) rispetto alla riga puntata;
  • absolute(int n): sposta il cursore di n righe a partire dalla prima riga dell'oggetto ResultSet;
Tutti i metodi detti sopra ritornano un valore booleano, true in caso di successo, altrimenti false. Altri metodi che solitamente utilizzo sono: deleteRow() che cancella la riga puntata, int findColumn(String ColumnName) che ritorna l'indice per accedere alla riga che contiene la colonna ColumnName e Array getArray(String ColumnName) che ritorna un array composto da tutte le occorrenze della colonna ColumnName. Come posso leggere i campi dati di una riga?

Finché il puntatore dell'oggetto ResultSet insiste su una riga dell'oggetto è possibile accedere ai campi dati attraverso uno dei tanti metodi getXXX (fornendo il nome della colonna come parametro). Esistono metodi per estrarre valori numerici (come int getInt(String ColumnName), float getFloat(String ColumnName), double getDouble(String ColumnName) e long getLong(String ColumnName)) e stringhe (come String getString(String ColumnName)). Tutto dipende dal tipo di informazione da estrarre (esistono altri metodi per altri tipi di dati, vi consiglio di dare uno sguardo alla documentazione in linea). Quasi tutti i metodi per la ricerca dei campi dati hanno anche la versione basata sulla ricerca numerica della colonna (anziché usare il nome della colonna viene usato un numero che fa riferimneto all'ordine numerico della colonna nella tabella).

while(srs.next()){
 String username=srs.getString("username");
 System.out.println("Username: "+username);
}
Se usiamo l'ultima versione di NetBeans IDE possiamo sviluppare fin da subito applicazioni JDBC, in caso contrario dobbiamo necessariamente scaricare il driver e configurarlo all'interno del nostro classpath.

giovedì 16 settembre 2010

Una rubrica con... MySQL e PHP

Ricordate il database realizzato con MySQL per una rubrica di contatti telefonici? Ho realizzato una semplice interfaccia con PHP e funzioni JavaScript per contrallare l'input. La cartella con tutti i file utili all'applicazione è scaricabile a questo indirizzo. Per rispettare l'input atteso dal database MySQL, nella pagina aggiungi.html, ho pensato a delle funzioni JavaScript che, attraverso l'uso di espressioni regolari, stabiliscono la presenza o meno di un possibile input. La struttura seguita è più o meno la seguente (per i numeri cambia il pattern in [0-9]{6,30}):

cognome=document.getElementById("NOME_CAMPO").value;
pattern=new RegExp("[a-zA-Z]{2,30}");
if (pattern.test(cognome)) return true;
else return false;

Il bottone per l'invio dei dati viene allora abilitato solo se le funzioni che controllano i campi dati del form (nome, cognome e numero di telefono) danno esito positivo! Tale controllo è poi necessario anche in fase di ricerca, nella pagina cerca.html. Particolare attenzione va infine fatta in fase di inserimento dati all'interno del database. Un contatto può avere uno o più numeri di telefoni! Pertanto, durante l'aggiunta di un numero occorre stabilire prima la presenza nel contatto all'interno della tabella contatti e successivamente procedere con l'aggiunta del nominativo, se quest'ultimo non è stato già inserito! Verificando che il numero sia effettivamente un nuovo numero per il contatto indicato. Tutto questo avviene all'interno del file aggiungi.php. Per eseguire l'applicazione su Apache2 occorre collocare l'intera cartella nella home del server (riga DocumenRoot del file /etc/apache2/sites-available/default) oppure effetuarne un collegamento simbolico in /etc/apache2/sites-enabled. Vi lascio qualche immagine dell'applicazione:








PHP e MySQL: le funzioni per il database

PHP e MySQL, entrambi prodotti open source, hanno sempre avuto un buon rapporto. Recentemente PHP ha aperto la porta anche ad altri database, aggiungendo per ognuno di essi un modulo. Il supporto di PHP verso MySQL rimane in ogni caso la strada più naturale per favorire l'integrazione di un database all'interno di un'applicazione web. Il modulo MySQL per PHP va abilitato all'interno della configurazione del server web (che per Apache2 avviene nel file /etc/php5/apache2/conf.d/mysql.ini). Questo ci permetterà l'accesso e la gestione del database attraverso l'uso di semplici istruzioni!
Prima di iniziare a interagire con un database occorre stabilire una connessione verso un server MySQL, ecco l'utilità della funzione mysql_connect($host,$user,$password). Dove:
  • $host: è il nome o indirizzo del server MySQL, può comprendere anche il numero di porta;
  • $user: è il nome dell'utente che si collega al server;
  • $password: è password assegnata all'utente di nome $user sull'host con nome o indirizzo $host;

Questa funzione restituisce un collegamento alla connessione verso il server MySQL, da usare nelle successive istruzioni. Solitamente a questa istruzione viene fatta seguire l'istruzione die(), che stampa un messaggio ed esce dallo script in caso di errore.

Se vi occorre una connessione persistente verso il database esiste, inoltre, la funzione mysql_pconnect(). Avete mai usato MySQL da riga di comando? La funzione mysql_connect() esegue per voi (in locale o in remoto) l'istruzione mysql -u NOME_UTENTE -p! Il secondo passaggio da compiere consiste nella scelta del database. Se da riga di comando, nel client MySQL, selezioniamo il database con use database NOME_DATABASE, in PHP dobbiamo invece affidarci alla funzione mysql_select_db($database,$connessione). Dove:

  • $database: è il nome del database;
  • $connessione: è il link alla connessione precedentemente ottenuto, in caso di successo, dall'istruzione mysql_connect();

Questa funzione ritorna un valore booleano: true in caso di successo, altrimenti false. Cosa deve accadere se la funzione non trova il database passato come argomento? Nella maggior parte dei casi tutto il codice PHP potrebbe non avere senso. Ecco allora come usare il valore restituito:

Adesso possiamo interrogare il database! Come? Con la funzione mysql_query($query,$connessione). Dove:

  • $query: è la stringa con la query da passare al server MySQL. Può pertanto contenere sia operazioni di inserimento che di modifica. E perché no? Anche istruzioni per la rimozione di occorrenze all'interno delle tabelle che compongono il database. Un limite a queste operazioni è ovviamente stabilito dai permessi dati all'utente che interagisce con il database;
  • $connessione: è il link alla connessione precedentemente ottenuto, in caso di successo, dalla funzione mysql_connect();
Questa funzione ritorna nel caso di SELECT, SHOW, DESCRIBE ed EXPLAIN un oggetto (o meglio, una risorsa) che contiene il risultato generato dalla query. Se la query contiene invece INSERT, UPDATE, DELETE o DROP ritorna true in caso di successo, altrimenti false.

Come possiamo esaminare il risultato restituito da mysql_query?
PHP offre più di una funzione! Prima ancora di andare a leggere il risultato contenuto nella variabile $risposta è forse utile conoscere il numero di righe che troveremo nell'oggetto. L'estrazione delle informazioni contenute in queste righe si esaurisce quasi sempre all'interno di un ciclo (come vedremo fra poco). Meglio stabilire fin dall'inizio se la query ha davvero generato un numero di righe utili, presumibilmente almeno uno! La funzione mysql_affected_rows($connessione) fa proprio questo: legge sulla connessione (quindi, variabile $connessione) il numero di righe generate come risposta all'ultima query di INSERT, UPDATE, REPLACE o DELETE.

Una delle funzioni PHP più usate per l'intercettazione delle informazioni all'interno delle righe ritornate da una query è la funzione mysql_fetch_array($risposta,TIPO_DI_INDIC) che ritorna un array di stringhe, indicizzato sia sui nomi dei campi dati che sugli indici numerici dell'array! Nei parametri passati alla funzione, $risposta è la variabile oggetto generata da mysql_query(), TIPO_DI_INDIC è una costante (opzionale) il cui valore stabilisce come deve essere indicizzato l'array. Può assumere uno di questi valori:
  • MYSQL_BOTH: indicizza l'array sia sui nomi dei campi dati che sugli indici dell'array;
  • MYSQL_ASSOC: indicizza l'array solo sui nomi dei campi dati;
  • MYSQL_NUM: indicizza l'array solo con indici numerici;
La funzione mysql_fetch_array() svolge anche un utile lavoro di scorrimento dell'array stesso. Attraverso un puntatore tiene la posizione dell'ultima riga letta, restituendone i valori a ogni chiamata. Quando non esistono più righe la funzione restituisce il valore false! Tale valore, allora, può essere restituito fin dall'inizio, se la query non genera alcun risultato!

Oppure:
La funzione mysql_fetch_assoc($risultati), a differenza di mysql_fetch_array(), raccoglie i risultati in un array associativo. Indicizzato, cioè, solo sui nome dei campi dati. Qualche volta può verificarsi un errore, perché non stamparlo sul monitor? Le funzioni mysql_errno($connessione) e mysql_error($connessione) ritornano rispettivamente il numero che identifica l'errore e la stringa che lo descrive (sono funzioni da non dimenticare se vogliamo aggiungere all'applicazione un database dedicato ai file di log). Se il nostro script va avanti per diverse righe dichiarando a destra e a sinistra variabili per contenere le risposte alle varie query è preferibile liberare le risorse occupate dalle variabili non più utilizzate con la funzione mysql_free($risultati) (che ritorna true in caso di successo, altrimenti false). A tale proposito, per non tenere sempre impegnate le risorse inutilizzate vi ricordo di chiuedere la connessione se non devono essere esguite sulla stessa altre query. La funzione mysql_close($connessione) chiude la connessione generata all'inizio da mysql_connect() e ora referenziata nella variabile $connessione. Questa deve essere una delle ultime istruzioni, non rimanete connessioni aperte! 
Altre utili funzioni PHP per MySQL sono: mysql_get_client_info() (che ritorna una stringa che descrive il tipo e la versione del client MySQL usato dall'utente che accede alla pagina PHP), mysql_info($connessione) (che ritorna informazioni sull'ultima query eseguita sul server MySQL) e mysql_stat($connessione) (che ritorna informazioni sullo stato del servizio e quindi del server MySQL).

giovedì 9 settembre 2010

Una rubrica con... MySQL

Avete mai pensato di implementare una rubrica telefonica? Si possono usare molte tecnologie e linguaggi di programmazione. Una soluzione sicuramente elegante e professionale consiste nell'adozione di un database e lasciare al linguaggio di programmazione l'interazione fra utente e dati. Con MySQL possiamo realizzare in pochi passi il database per la nostra applicazione, se questa cosa vi interessa vi dico come procedere.
La rubrica che ho in mente è abbastanza semplice, deve contenere il nome e il cognome dei nostri contatti e l'eventuale numero di telefono. Un contatto può non avere necessariamente un numero di telefono. Una domanda che merita fin dall'inizio una risposta è la seguente: quanti numeri di telefono può avere un contatto? La risposta a questo quesito condiziona la progettazione del database. Se la cardinalità del numero di telefono di un contatto è fissata ad 1 non si hanno molti problemi, un unica tabella raccoglierà: nome, cognome e numero di telefono. Sappiamo bene che questa limitazione non ci rende felici. Ecco allora che modelleremo il database per contenere zero o più numeri telefonici (contenti?).
Nello schema che vi propongo ho individuato due entità: contatti e rubrica. Nell'entità contatti ho messo gli attributi nome e cognome. In rubrica, invece, ho il solo attributo telefono. Un numero di telefono può essere assegnato ad uno o più contatti.


Nell'entità contatti ho poi introdotto la chiave primaria id, in rubrica ho scelto come chiave primaria l'attributo telefono. Queste sono le istruzioni SQL per la descrizione del database:
CREATE DATABASE rubrica_telefonica;
USE rubrica_telefonica;
CREATE TABLE contatti (
 id INT(3) NOT NULL AUTO_INCREMENT,
 nome VARCHAR(30) NOT NULL,
 cognome VARCHAR(30) NOT NULL,
 UNIQUE (nome, cognome),
 PRIMARY KEY (id)
);

CREATE TABLE rubrica (
 telefono VARCHAR(30) NOT NULL,
 contatto INT(3) NOT NULL REFERENCES contatti(id),
 PRIMARY KEY (telefono,contatto)
);
Ho poi aggiunto i permessi per permettere a un utente l'interazione con il database e le tabelle appena create:
GRANT INSERT,SELECT
ON rubrica_telefonica.*
TO php_user IDENTIFIED BY 'php_user';
Osservazione: grazie alla riga UNIQUE (nome, cognome) non potranno esistere occorrenze dell'entità contatti che hanno lo stesso attributo nome e cognome. Potranno tuttavia esistere contatti con lo stesso nome o cognome. Il database per lo nostra applicazione è pronto per essere usato. Vi riporto, per comodità, qualche utile query per l'estrazione dei dati:

Ricerca 1: per nome
SELECT telefono, cognome
FROM rubrica R JOIN contatti C ON R.contatto=C.id
WHERE C.nome='Luca'
Ricerca 2: per cognome
SELECT telefono, nome
FROM rubrica R JOIN contatti C ON R.contatto=C.id
WHERE C.cognome='Petrosino'
Ricerca 3: per nome e cognome
SELECT telefono, cognome
FROM rubrica R JOIN contatti C ON R.contatto=C.id
WHERE C.nome='Luca' AND C.cognome='Petrosino'
Ricerca 4: per numero
SELECT nome, cognome
FROM rubrica R JOIN contatti C ON R.contatto=C.id
WHERE R.telefono='0123456789'
Ricerca 5: il numero finisce per... 789
SELECT nome, cognome, telefono
FROM rubrica R JOIN contatti C ON R.contatto=C.id
WHERE R.telefono LIKE "%789"
Ricerca 6: il numero inizia per... 012
SELECT nome, cognome, telefono
FROM rubrica R JOIN contatti C ON R.contatto=C.id
WHERE R.telefono LIKE "012%"
Ricerca 7: contiene il numero... 345
SELECT nome, cognome, telefono
FROM rubrica R JOIN contatti C ON R.contatto=C.id
WHERE R.telefono LIKE "%012%"

venerdì 3 settembre 2010

MySQL: la gestione dei permessi

"Chi può fare cosa?" Una domanda che ricorre spesso in informatica. In MySQL la gestione dei permessi per gli utenti avviene con le clausole GRANT e REVOKE, sulla riga di comando del client. Per la concessione di privilegi bisogna osservare la seguente sintassi:
GRANT priv_type [column_list][,priv_type [column_list]]...
ON {*|*.*|db_name.*|db_name.tbl_name}
TO username [IDENTIFIED BY[PASSWORD] 'password'][,IDENTIFIED BY [PASSWORD] 'password']
Per la rimozione di permessi, invece:
REVOKE priv_type [column_list][,priv_type [column_list]]...
ON {*|*.*|db_name.*|db_name.tbl_name}
FROM username
I permessi che possiamo assegnare o revocare con con GRANT o REVOKE possono essere identificati dalle seguenti parole chiavi: SELECT (lettura dei valori in tabella), INSERT (scrittura di nuovi valori in tabella), UPDATE (modifica di valori in tabella), DELETE (eliminazione di valori presenti in tabella). Tali permessi, inoltre, possono essere combinati se separati con una virgola. Con la parola chiave ALL si abilitano (oppure vengono negati) all'utente tutti i permessi. Altri utili permessi sono: CREATE (per creare tabelle e/o database) e DROP (per cancellare tabelle e/o database).
Con la clausola ON possiamo estendere il permesso a uno o più database, e tabelle. Il simbolo * concede un permesso a tutti i database, la notazione *.* la estende invece a tutte le tabelle di tutti i database. Si può ovviamente essere più precisi se indichiamo il nome del database. La stringa nome_database.* estende il permesso a tutte le tabelle del database con con nome nome_database. Più precisa, infine, è la sintassi nome_database.nome_tabella che denota la tabella con nome nome_tabella del database con nome nome_database.
L'ultima clausola, TO, convoglia i permessi (da far valere nei confronti di tabelle e/o database) verso l'account di uno o più utenti. E' in questa clausola che occorre fornire il nome dell'utente coinvolto nella gestione dei permessi, facendo poi seguire alla parola chiave IDENTIFIED BY la password usata dallo stesso! Inoltre, se al nome dell'utente, dopo il simbolo @, indichiamo il nome dell'host da usare per la connessione obblighiamo l'utente a collegarsi al database sempre attraverso lo stesso host indicato nella GRANT. Se non specificato, di default, un utente può collegarsi da qualsiasi host. Per la revoca dei permessi, con REVOKE, è sufficiente indicare solo il nome dell'utente.

venerdì 2 luglio 2010

Linux: installazione di MySQL

In questo breve articolo vi propongo l'installazione di MySQL su Xubuntu 9.10 (e Ubuntu in generale) e la risoluzione di alcuni possibili errori. Come sempre preferisco procedere attraverso la riga di comando. MySQL si compone di un client e di un server, ecco quindi che per l'installazione occorre dare il seguente comando:

sudo apt-get install mysql-server mysql-client

Nei repository di Ubuntu è presente la versione 5.1 di MySQL. Chi vuole può anche provare l'installazione del pacchetto mysql-navigator, che fornisce un interfaccia grafica ai database (il cui sviluppo è tuttavia fermo da tempo). Durante la fase di configurazione di MySQL ci verrà presentata una schermata come quella che segue:


L'installer ci sta chiedendo di digitare la password per l'account root che userà MySQL (per sicurezza ci verrà chiesto due volte). Dopo l'installazione il server di MySQL viene eseguito di default ed è quindi in attesa di connessioni al database. Possiamo allora avviare una sessione con il client di MySQL digitando il seguente comando: mysql -u root -p, dove con l'opzione -u si specifica il nome dell'utente e con -p si dice al client che verrà indicata la password per l'account su una riga a parte (con mysql --help si ottiene un elenco dei comandi per il client). Ecco un'immagine:


Per fermare il server MySQL va lanciato il comando sudo /etc/init.d/mysql stop, per avviarlo sudo /etc/init.d/mysql start e per riavviarlo sudo /etc/init.d/mysql restart. Veniamo a qualche errore (noto) che si può presentare usando MySQL.
L'errore "Could not start service MySQL. Error:0" si verifica quando è in esecuzione un servizio con lo stesso nome, MySQL appunto. Può ad esempio capitare quando riavviamo il server e il precedente processo non si è ancora chiuso (oppure non si chiude). Se la situazione permane, pur aspettando, si consiglia di disinstallare MySQL e di ripetere l'installazione.
L'errore "ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)" si verifica quando la password digitata nel client non è quella nota al server di MySQL.


Sicuri di ricordare bene la password? Sicuri di averla inserita in fase di installazione? In quest'ultimo caso provate a premere "Invio" quando vi viene chiesta la password, se funziona allora la password non era stata impostata!
Se non riuscite a ricordare la password inserita durante l'installazione è possibile ripristinarne il valore in questo modo: dare prima il comando sudo killall mysqld (per terminare il processo) e successivamente mysqld --skip-grant-tables (che avvia il processo daemon senza le tabelle per i permessi), attendere che le scritte sul terminale si stabilizzino e lasciare aperta questa finestra di terminale; aprire una nuova finestra e avviare il client MySQL con mysql; nella finestra appena aperta collegarsi al database di nome mysql con il comando use mysql; (ogni istruzione MySQL termina con un punto e virgola); aggiornare il campo password dell'utente con UPDATE user SET password=password("ciao") WHERE user="root"; (che imposta come password per l'utente root la stringa ciao); per finire date i comandi flush privileges; ed exit, (chiudete la prima finestra, quella bloccata). Ecco qualche immagine di supporto, qui potete vedere la finestra bloccata:


Questa, invece, è la finestra usata per collegarsi al client di MySQL per impostare la nuova password:

sabato 26 giugno 2010

Un ripasso di SQL e MySQL

L'informazione è ormai manipolata in ogni contesto e negli ultimi anni si è affidata sempre più a strumenti informatici. La raccolta e la manipolazione dell'informazione è resa possibile grazie all'uso di database che in ambito informatico viene molto spesso chiamato DBMS: database management system. Nel corso degli anni sono state introdotte diverse strutture per DBMS: gerarchico, reticolare, relazionale, ad oggetti e semantico. In questo ripasso ci occuperemo di un DBMS relazionale: MySQL.
MySQL è un DBMS distribuito con licenza GPL, chiunque può accedere al codice sorgente del DBMS per apportare modifiche. In un database relazionale l'informazione viene memorizzata sotto forma di tabelle, oguna di queste può poi essere messa in relazione con altre tabelle del database. MySQL si basa sul noto modello client/server e prevede pertanto un'interfaccia di tipo client ed una di tipo server. L'interfaccia che MySQL offre di default al programmatore è a riga di comando, chi vuole può in ogni caso installare quella grafica.
Il client interroga o comanda il server (se ha i permessi per farlo) affinchè quest'ultimo restituisca l'informazione cercata (se presente nel database) oppure aggiunga al database una nuova informazione. L'interrogazione di un database (sia per la ricerca dei dati che per la loro immissione), detta query, avviene con un opportuno linguaggio (standard) noto con il nome di SQL (Struct Query Language). Per interagire con il server MySQL bisogna per prima cosa lanciare il daemon. Vediamo come farlo con l'Aspire One (che usa come sistema operativo di default Linux Linpus Lite, le istruzioni per l'avvio del server e client MySQL si adattano comunque a ogni sistema operativo Linux-like):

[user@localhost ~]$ sudo service mysqld start
Starting MySQL: [ OK ]
[user@localhost ~]$

Altri possibili comandi sono: stop (per fermare il daemon) e restart (per riavviare il daemon). L'interfaccia client viene invece avviata con il comando:

[user@localhost ~]$ sudo mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 27
Server version: 5.0.45 Source distribution

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>

Il prompt è ora in attesa di comandi. Se vogliamo ad esempio conoscere i nomi dei database che di default si trovano presso il server MySQL va digitato questo comando:

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| test |
+--------------------+
3 rows in set (0.00 sec)

mysql>

Per selezionare un database digitiamo:

mysql> use test;
Database changed
mysql>

Prima ancora di vedere come creare database e tabelle, diamo ora uno sguardo ai tipi di dati che MySQL offre al programmatore per descrivere la propria realtà di riferimento.

Tipi di dati numerici
MySQL offre, per i numeri interi, questi tipi di dati:
  • TINYINT, occupa 1 byte;
  • SMALLINT, occupa 2 byte;
  • MEDIUMINT, occupa 3 byte;
  • INT, occupa 4 byte;
  • BIGINT, occupa 8 byte;
Per i numeri decimali, invece:
  • FLOAT, occupa 4 byte;
  • DOUBLE, occupa 8 byte;
  • DECIMAL(M,D), occupa M+2byte;
Il tipo DECIMAL permette di specificare con M e D, rispettivamente, il numero totale delle cifre che formano l'intero numero decimale e il numero di cifre per la parte decimale. I tipi di dati appena visti possono prevedere o meno delle opzioni. Con l'opzione UNSIGNED, ad esempio, si indica al server MySQL che l'attributo in questione non può avere valori negativi. Con l'opzione ZEROFILL si indica al server MySQL di allineare i numeri (in verticale) di un attributo riempiendolo con tanti zeri. Con l'opzione AUTOINCREMENT si imposta per l'attributo di una tabella una particolare proprietà: ad ogni istanza dello stesso il valore dell'attributo viene incrementato di un'unità.

Tipi di dati per testo
Per i tipi di dato orientati alla gestione del testo, ossia per le stringhe, MySQL prevede due utili tipi:
  • CHAR(M), occupa M byte;
  • VARCHAR(M), occupa M byte;
L'occupazione (fissa) in memoria viene specificata dal programmatore. Gli attributi di una tabella descritti dal tipo CHAR occupano in memoria uno spazio pari ad M byte. Al contrario invece, gli attributi di una tabella descritti dal tipo VARCHAR occupano un numero di byte variabile che al più può essere di M byte. In altre parole, con VARCHAR l'occupazione in memoria dell'informazione (ovvero della stringa in questo caso) varia a seconda dell'occorrenza.
I tipi di dati appena visti consentono di inserire in un database stringhe non più lunghe di 255 caratteri. Se tale limite non è sufficiente al nostro progetto possiamo allora adoperare altri tipi di dati come: BLOB e TEXT.

Tipi di dati per le date
L'informazione molto spesso deve poter trattare date e ore del giorno. A tale proposito MySQL offre due tipologie di dati, quelli orientati appunto alle date e quelli orientati esclusivamente ad una parte della data. Per la gestione di date complete (che comprendono quindi gli attributi: anno, mese, giorno, ora,minuto e secondo) si può fare riferimento ai tipi: TIMESTAMP (che occupa 14 byte) e DATETIME (che occupa 8 byte). Per la gestione parziale di una data si può invece fare riferimento ai tipi: DATE, TIME e YEAR.
Per il tipo TIMESTAMP bisogna considerare il seguente formato: AAAAMMGGHHMMSS. Passando a TIMESTAMP un parametro per modificare l'occupazione in byte del suddetto formato se ne modifica allora il contenuto. Ad esempio, con TIMESTAMP(12) vado ad escludere gli ultimi 2 byte, quelli che trattano cioè i secondi, e modifico in questo modo il formato: AAAAMMGGHHMM. Ancora, con TIMESTAMP(10) vado ad escludere gli ultimi 4 byte, quelli cioè che trattano i secondi e i minuti, e modifico in questo modo il formato: AAAAMMGGHH.
Se non siamo interessati per intero alla data di un evento possiamo allora usare il tipo DATE (che occupa 3 byte). La restante parte della data, quella che si occupa delle ore, dei minuti e dei secondi, è invece gestita dal tipo TIME (che occupa 3 byte).

DDL, data definition language
Il DDL è il linguaggio usato per definire gli schemi logici (ed eventualmente le autorizzazioni per l'accesso ai dati). Esso si fonda essenzialmente su tre parole chiavi: create (creare), drop (cancellare) e alter (modificare). La parola chiave create permette di creare oggetti nella base di dati:

create table;
create domain;
create view;
create constraint;

Per la creazione di un database:

mysql> create database segreteria;
Query OK, 1 row affected (0.00 sec)

mysql>

Per la creazione di tabelle si ricorre al comando create table. Ecco un esempio:

mysql> create table studente(
-> matricola varchar(15),
-> nome varchar(20),
-> cognome varchar(20),
-> nascita date);
Query OK, 0 rows affected (0.46 sec)

mysql>

In SQL ogni istruzione termina con il carattere ;. Il carattere ,, invece, viene usato per separare la descrizione di una colonna da quella successiva. Al comando create table deve seguire il nome da assegnare alla tabella. La descrizione dei campi che compongono la tabella deve avvenire all'interno delle parentesi ( e ). I campi dati sono specificati da un nome e dal tipo che meglio li rappresenta. I vincoli da assegnare ad un campo dati vengono specificati subito dopo il tipo se essi riguardano esclusivamente il campo stesso, oppure su una riga a parte se interessano più campi dati della tabella.

Valori di default per i tipi di dati
In alcuni casi può essere utile assegnare ad un tipo di dato un valore di default da usare qualora il valore, in fase di immissione, non fosse specificato dall'utente. Quando si vuole seguire questa strada occorre allora usare la parola chiave default e far seguire dopo di questa il valore di default, ecco un esempio:

mysql> create table studente(
-> matricola varchar(15),
-> nome varchar(20),
-> cognome varchar(20),
-> nascita date,
-> esami tinyint default 0);
Query OK, 0 rows affected (0.27 sec)

mysql>

Vincoli intrarelazionali
Sono vincoli (o condizioni) che coinvolgono una sola relazione e che devono essere verificati ad ogni istanza della base di dati, ecco un elenco:
  • not null, indica che il valore nullo non è ammeso come valore dell'attributo;
  • unique, impone che i valori dell'attributo all'interno della tabella siano unici. Pertanto, righe differenti di una tabella non possono avere gli stessi valori per quell'attributo (per il valore null si fa invece un'eccezione);
  • primary key, si tratta di un vincolo che è possibile esprimere in tabella una sola volta poichè identifica la chiave primaria per la tabella stessa. Si esprime sulla riga dell'attributo oppure su una riga separata nel caso in cui la chiave primaria è composta da più attributi.
Rivediamo, allora, l'esempio precedente adoperando ora i vincoli intrarelazionali appena visti:

mysql> create table studente(
-> matricola varchar(15),
-> nome varchar(20) not null,
-> cognome varchar(20) not null,
-> nascita date,
-> esami tinyint default 0,
-> primary key(matricola,cognome,nome));
Query OK, 0 rows affected (0.24 sec)

mysql>

All'interno di schemi scritti, la primary key viene identificata dall'attributo sottolineato, ecco un esempio:

studente(matricola, nome, cognome, nascita, esami)

Vincoli interrelazionali
Sono vincoli che coinvolgono più relazioni creando un legame tra i valori di un attributo della tabella corrente (detta tabella interna) e i valori di un attributo di un'altra tabella (detta tabella esterna). Il valore di un attributo alla quale si fa riferimento deve essere almeno unique (molto spesso accade che questo valore è primary key per la tabella esterna e quindi è anche unique, una chiave primaria è infatti anche unica nella tabella!).

mysql> create table studente(
-> nome varchar(20),
-> cognome varchar(20) not null,
-> matricola char(6) primary key);
Query OK, 0 rows affected (0.01 sec)

mysql> create table esame(
-> studente char(6) references studente(matricola),
-> voto smallint not null check((voto>=18) AND (voto<=30)), -> corso varchar(30) primary key);
Query OK, 0 rows affected (0.17 sec)

mysql>

Politiche di reazione
Cosa deve fare il database quando il sistema rileva una violazione di un vincolo? E' possibile assegnare al sistema alcune politiche di reazione non appena si verifica la violazione di un vincolo. La violazione di un vincolo, ad esempio, può avvenire cancellando o aggiornando un vincolo. Di default si assegna la politica no action ad un vincolo, se ciò deve invece essere modificato o impedito si ricorre a istruzioni così strutturate:

on delete|update cascade|set null|set default| no action

dove con:
  • cascade, il valore viene riportato su tutte le righe della tabella esterna;
  • set null, si assegna il valore null all'attributo referente;
  • set default, si assegna il valore di default all'attributo referente;
  • no action, l'azione di modifica o cancellazione non viene consentita;

Drop e alter
Questo comando permette di rimuovere componenti da un database. Tale comando in genere può non funzionare immediatamente. Il sistema infatti (la logica data a MySQL) chiede all'utente di svuotare prima la tabella, solo dopo averla svuotata può infatti essere cancellata. Questo discorso si applica anche a un database: prima si svuotano e cancellano le tabelle poi si cancella il databate!

mysql> drop table esame;
Query OK, 0 rows affected (0.00 sec)

mysql>

Nell'esempio il comando riesce proprio perché la tabella esiste ma è vuota! Il comando alter, invece, permette di modificare domini e schemi di tabelle.

DML, data manipulation language
L'informazione, una volta inserita nel database, va cercata e eventualmente elaborata. In questa sezione ci occuperemo dell'interrogazione di un database. Riporto qui di seguito gli esempi visti a lezione, per alcuni di questi faremo riferimento al seguente schema logico:

impiegato(nome, cognome, dipart, ufficio, stipendio, città)
dipartimento(nome, indirizzo, città)

1. Estrarre lo stipendio degli impiegati di cognome "Rossi":

select stipendio
from impiegato
where cognome='Rossi'

2. Estrarre tutte le informazioni relative agli impiegati di cognome "Rossi":

select *
from impiegato
where cognome='Rossi'

3. Estrarre lo stipendio mensile dell'impiegato che ha cognome "Bianchi":

select stipendio/12
from impiegato
where cognome='Bianchi'

Questo perché nella clausola select possono comparire generiche espressioni sul valore degli attributi di ciascuna riga selezionata.

4. Estrarre i nomi degli impiegati e le città in cui lavorano:

select I.nome, I.cognome, D.città
from impiegato as I,dipartimento as D
where I.dipart=D.nome

L'operatore punto identifica alla sua sinistra le tabelle da cui vengono estratti gli attributi (indicati alla destra del punto). Una tabella può essere rinominata con un opportuno alias preceduto dalla parola chiave as (che può anche essere omesso). In questo esempio la clausola select opera sul prodotto cartesiano delle tabelle in essa elencate e separate dal carattere ,. Nella clausola where in tal caso si specifica la condizione che lega le due tabelle.

5. Estrarre il nome e il cognome degli impiegati che lavorano nell'ufficio 20 del dipartimento di amministrazione:

select nome, cognome
from impiegato
where ufficio=20 and dipar='amministrazione'

Nella clausola where sono possibili gli operatori di relazione "=", ">", "<", "<=" e ">=" oltre a quelli logici and, or, e not. L'ordine con cui considerare tali operatori va eventualmente indicato con opportune parentesi se risulta essere diverso da quello previsto di default.

6. Estrarre i nomi e cognomi degli impiegati che lavorano nel dipartimento amministrazione o nel dipartimento produzione:

select nome, cognome
from impiegato
where dipart='amministrazione' or dipart='produzione'

7. Estrarre i nomi propri degli impiegati di cognome "Rossi" che lavorano nei dipartimenti amministrazione e produzione:

select nome
from impiegato
where cognome='Rossi' and (dipart='amministrazione' or dipart='produzione')

8. Estrarre gli impiegati che hanno un cognome che ha una "o" in seconda posizione e finisce per "i":

select *
from impiegato
where cognome like '_o%i'

L'operatore like permette un confronto di stringhe con stringhe in cui compaiono caratteri speciali. Il carattere "_" indica la possibilità di un qualsiasi carattere. Il carattere "%" indica l'occorrenza di una qualsia stringa.

9. Estrarre le città delle persone il cui cognome è "Rossi", presentando eventualmente più volte lo stesso valore di città:

persona(CodFiscale, nome, cognome, città)

select città
from persona
where cognome='Rossi'

10. Estrarre le città delle persone con cognome "Rossi" facendo comparire ogni città al più una sola volta:

persona(CodFiscale, nome, cognome, città)

select distinct città
from persona
where cognome='Rossi'

Per elimmnari i duplicati di un valore di un campo si fa procedere l'attributo dalla parola chiave distinct.

11. Estrarre il contenuto della tabella automobile ordinandolo in base alla marca e al modello:

automobile(targa, marca, modello, nropatente)

select *
from automobile
order by marca desc, modello

L'SQL permette di specificare come ordinare le righe del risultato di una interrogazione tramite la clasuola order by, che precede il nome dell'attributo, e facendo quindi seguire lo stesso attributo dalla parola chiave asc o desc.

12. Estrarre i nomi degli impiegati e le città in cui lavorano:

select I.nome, I.cognome, D.città
from impiegato as I join dipartimento as D on I.dipart=D.nome

Esiste una sintassi alternativa per la specifica dei join che permette di distinguere le condizioni dell'interrogazione da quelle di join. Usando questa sintassi la condizione che lega le tabelle non compare nella clasuola where.

13. Estrarre i guidatori con le automobili loro associate, mantenendo nel risultato anche i guidatori senza automobile:

automobile(targa, marca, modello, nropatente)
guidatore(nome, cognome, nropatente)

select distinct città
from guidatore G left join automobile A on (g.nropatente=A.nropatente)

Esistono diversi tipi di join: inner join, right outer join, left outer join e full outer join. L'outer join effettua un join mantenendo nel risultato tutte le righe di una tabella. Il left join fornisce come risultato il join esteso con le righe della tabella che compare a sinistra. Il right join fornisce come risultato il join esteso con le righe della tabella che compare a destra. Il full join restituisce il join interno esteso con le righe escluse di entrambe le tabelle.

14. Estrarre tutti i guidatori e tutte le auto, mostrando tutte le relazioni esistenti tra di essi:

select nome, cognome, G.nropatente, targa, marca, modello
from guidatore as G full join automobile as A on (G.nropatente=A.nropatente)

15. Estrarre il numero di impiegati del dipartimento produzione:

select count(*)
from impiegato
where dipart='produzione'

count(*) è un operatore in grado di contare il numero di righe che superano il filtro imposto dalla clausola where.

16. Estrarre il numero di diversi valori dell'attributo stipendio fra tutte le righe della tabella impegato:

select count(distinct stipendio)
from impiegato

17. Estrarre gli stipendi minimo, massimo e medio fra quelli di tutti gli impiegati:

select min(stipendio), max(stipendio), avg(stipendio)
from impiegato

18. Estrarre il massimo stipendio tra quelli degli impiegati che lavorano in un dipartimento con sede a Milano:

select max(stipendio)
from impiegato as I, dipartimento as D
where I.dipart=D.nome and città='Milano'

19. Estrarre la somma degli stipendi di tutti gli impiegati dello stesso dipartimento:

select dipart, sum(stipendio)
from impiegato
group by dipart

L'operatore aggregato sum può essere applicato ad un gruppo di righe mediante l'uso della clausola group by. Tale clausola ammette come argomento un insieme di attributi sicché la query raggrupperà le righe che possiedono effettivamente gli stessi valori per quell'attributo.

20. Estrarre i dipartimenti per cui la media degli stipendi degli impiegati che lavorano nell'ufficio 20 è superiore a 25 mila euro:

select dipart
from impiegato
where ufficio='20'
group by dipart
having avge(stipendio) > 25

21. Estrarre i nomi ed i cognomi di tutti gli impiegati, ad eccezione di quelli appartenenti al dipartimento amministrazione e mantenendo i duplicati:

select nome
from impiegato
where dipartimento<>'amministrazione'

union all

select cognome
from impiegato
where dipartimento<>'amministrazione'

22. Estrarre gli impiegati che lavorano in dipartimenti situati a Firenze:

select *
from impiegato
where dipartimento = any (select nome
from dipartimento
where città='Firenze')

Questo esempio ci permette di introdurre le query nidificate. In tal caso, una query interna seleziona il nome del dipartimento (situato a Firenze) mentre quella esterna confronta il risultato dell'interrogazione con l'attributo dipartimento della tabella impiegato convalidando il risultato nel caso in cui esista in essa almeno un valore valido.

Inserimento di valori in una tabella
Il comando SQL per l'inserimento di nuovi valori in tabella è insert. Per l'inserimento dei valori occorre conoscere l'ordine degli attributi appartenenti alla tabella. Un modo per inserire nuovi valori è il seguente:

insert into impiegato('Mario','Rossi','produzione',20,25000,'Firenze')

Per modificare un valore già presente in tabella si adopera invece il comando update:

update impiegato
set stipendio=20000
where nome='Mario' and cognome='Rossi'


Cancellare valori in una tabella
L'eliminazione di un valore all'interno di una tabella va fatto con il comando delete from. Ecco un esempio:

delete from impiegato
where nome='Mario' and cognome='Rossi'

Se l'argomento della clausola where non viene specificato il comando cancellerà di default tutte le righe della tabella indicata. MySQL offre molte altre cose, a tale proposito vi segnalo il sito di riferimento presso cui rimediare il software e la relativa documentaziione: http://www.mysql.it/.