BASI DATI.pdf
Document Details
Full Transcript
BASI DATI: Sommario Nessuna voce di sommario trovata. 1.0 Sistemi Informativi e Sistemi Informatici Nello svolgimento di ogni attività, la disponibilità di informazioni e la capacità di gestirle efficacemente sono essenziali, sia a livello individuale sia nelle organizzazioni di ogni dimensione. O...
BASI DATI: Sommario Nessuna voce di sommario trovata. 1.0 Sistemi Informativi e Sistemi Informatici Nello svolgimento di ogni attività, la disponibilità di informazioni e la capacità di gestirle efficacemente sono essenziali, sia a livello individuale sia nelle organizzazioni di ogni dimensione. Ogni organizzazione, infatti, dispone di un sistema informativo che organizza e gestisce le informazioni necessarie per perseguire i propri scopi. È importante notare che l’esistenza di un sistema informativo è solo in parte legata alla sua automatizzazione: i sistemi informativi esistono da molto prima dell’era dei calcolatori elettronici (ad esempio, archivi bancari o anagrafici). Quando parliamo della parte automatizzata di un sistema informativo, usiamo il termine sistema informatico. Negli ultimi decenni, la diffusione capillare dell’informatica ha fatto sì che molti sistemi informativi siano anche sistemi informatici, ma non bisogna confondere i due concetti. Nelle attività umane più semplici, le informazioni possono essere gestite anche senza rappresentazioni complesse, utilizzando lingua scritta, numeri o disegni. In altri casi, le informazioni non vengono nemmeno formalizzate ma restano memorizzate a livello mentale. Con la crescita della complessità delle attività, è diventato necessario organizzare e codificare meglio le informazioni. Nei sistemi informatici, la rappresentazione e codifica delle informazioni diventa cruciale. Le informazioni vengono tradotte in dati, che devono essere interpretati per fornire significato. I dati da soli non hanno alcun significato; è solo attraverso la loro interpretazione e correlazione che essi diventano informazioni. Le informazioni, a loro volta, arricchiscono la nostra conoscenza del mondo. A livello pratico, possiamo considerare i dati come simboli (numeri, stringhe, ecc.) che richiedono elaborazione per acquisire significato, diventando così informazioni utili. Un esempio semplice chiarisce questa distinzione: la stringa "Ferrari" e il numero "8", scritti su un foglio di carta, sono dati. Da soli, non significano nulla. Tuttavia, nel contesto di un ristorante durante una notte di Capodanno, questi dati possono rappresentare un’ordinazione di una bottiglia di spumante della marca Ferrari, da addebitare alla camera numero 8. Questo esempio dimostra come, una volta aggiunte informazioni contestuali, i dati si trasformino in informazioni. Introducendo il concetto di base di dati, possiamo definire questa come una collezione di dati utilizzata per rappresentare le informazioni necessarie per un sistema informativo. Nei sistemi informativi complessi, una base di dati diventa uno strumento fondamentale per gestire grandi quantità di dati in modo efficiente. Inoltre, i dati tendono a essere molto più stabili nel tempo rispetto alle procedure che operano su di essi. Per esempio, i dati bancari sono rimasti invariati per decenni, mentre le procedure che li gestiscono cambiano più frequentemente. Quando una procedura viene sostituita, la nuova "eredita" i dati della vecchia, spesso con trasformazioni. Questa stabilità dei dati porta alla conclusione che essi rappresentano una risorsa fondamentale per l'organizzazione: un vero e proprio patrimonio da sfruttare e proteggere. 2.0 Basi di Dati: Un Pilastro dell'Informazione Fin dagli albori dell'informatica, l'attenzione verso la gestione dei dati ha sempre rappresentato un aspetto centrale. Tuttavia, solo a partire dalla fine degli anni Sessanta sono stati sviluppati sistemi software specifici per la gestione dei dati, e, ancora oggi, alcune applicazioni non ne fanno uso. In assenza di un software dedicato, la gestione dei dati è stata tradizionalmente affidata a linguaggi di programmazione come C e Fortran o, in tempi più recenti, a linguaggi a oggetti come C++ e Java. Esistono ancora applicazioni scritte in COBOL, un linguaggio degli anni Sessanta ormai superato. L'approccio convenzionale alla gestione dei dati si basa su archivi o file per memorizzare i dati in modo persistente su memoria di massa. Sebbene un file permetta di memorizzare e cercare dati, esso offre solo semplici meccanismi di accesso e condivisione. In questo modello, ogni programma gestisce i propri file "privati", il che comporta duplicazioni dei dati e possibili incoerenze quando dati replicati non vengono aggiornati in modo sincrono. Un esempio concreto di questa problematica si può osservare nel contesto universitario, dove diverse entità gestiscono informazioni sui docenti. L'ufficio del personale mantiene i dati relativi alla carriera accademica, le presidenze delle facoltà gestiscono gli incarichi di insegnamento, e l'ufficio stipendi utilizza tali informazioni per calcolare le retribuzioni. Se ognuno di questi uffici gestisse le proprie informazioni separatamente, si creerebbero incoerenze tra le varie copie dei dati. Le basi di dati sono state sviluppate per risolvere questi problemi, permettendo la gestione integrata delle informazioni e riducendo ridondanza e incoerenze. Un sistema di gestione di basi di dati (DBMS) è un software progettato per gestire collezioni di dati di grandi dimensioni, condivise tra più utenti, e garantire che siano persistenti, affidabili e private. Le principali caratteristiche di un DBMS sono: Grandi dimensioni: Le basi di dati possono raggiungere dimensioni enormi, misurate in centinaia o migliaia di terabyte. I DBMS devono essere in grado di gestire queste collezioni di dati, che spesso superano la capacità della memoria centrale disponibile. Condivisione dei dati: I dati devono essere accessibili a più utenti o applicazioni in modo sicuro, riducendo la ridondanza e prevenendo inconsistenze. I DBMS includono un controllo di concorrenza per garantire che le operazioni simultanee non causino conflitti. Persistenza: I dati rimangono disponibili anche al termine dell'esecuzione di un programma. A differenza dei dati temporanei gestiti in memoria centrale, i dati in un DBMS sono permanenti. Affidabilità: I DBMS offrono meccanismi di salvataggio e ripristino per proteggere i dati da errori hardware o software, garantendo che i dati cruciali, come quelli finanziari, siano preservati nel tempo. Privatezza: I DBMS regolano l'accesso ai dati attraverso meccanismi di autorizzazione, permettendo solo ad utenti autorizzati di eseguire determinate operazioni sui dati. Efficienza ed efficacia: Un DBMS deve essere efficiente nell'utilizzo delle risorse di sistema e efficace nel rendere produttive le attività degli utenti. Sebbene sia possibile gestire dati persistenti e di grandi dimensioni utilizzando file semplici, i DBMS offrono funzionalità superiori, come l'accesso condiviso e la gestione centralizzata. Ad esempio, in un'università, un sistema ideale prevedrebbe una singola base di dati condivisa tra tutti gli uffici per gestire le informazioni sui docenti, i corsi, le facoltà e gli aspetti amministrativi. Tuttavia, a volte è necessario mantenere basi di dati separate, come nel caso delle informazioni riservate sui docenti rispetto a quelle pubbliche sui corsi, che possono essere accessibili tramite il sito web dell'ateneo. 2.1 Modelli dei dati Un modello dei dati è un insieme di concetti utilizzati per organizzare i dati di interesse e descriverne la struttura in modo che possa essere compresa e gestita da un elaboratore. Ogni modello dei dati fornisce dei meccanismi di strutturazione, che sono analoghi ai costruttori di tipo dei linguaggi di programmazione. Questi meccanismi permettono di definire nuovi tipi basati su tipi predefiniti (elementari) e costruttori di tipo. Ad esempio, nel linguaggio di programmazione C, è possibile definire nuovi tipi attraverso costruttori come struct, union, enum, e pointer (*). Il modello relazionale, introdotto formalmente agli inizi degli anni Settanta e diffusosi durante il decennio successivo, rappresenta il modello dei dati più utilizzato e viene considerato il modello di riferimento nel campo della gestione dei dati. Il modello relazionale dei dati consente di definire tipi attraverso il costruttore relazione, che organizza i dati in insiemi di record a struttura fissa. Una relazione è comunemente rappresentata come una tabella: le righe rappresentano specifici record, mentre le colonne corrispondono ai campi di ciascun record. È importante notare che l'ordine delle righe e delle colonne in una tabella non ha rilevanza nel contesto del modello relazionale. 2.2 Schemi e istanze Nelle basi di dati, si distingue tra una parte sostanzialmente invariante nel tempo, detta schema della base di dati, e una parte variabile nel tempo, chiamata istanza o stato della base di dati. Lo schema rappresenta le caratteristiche strutturali dei dati, mentre l'istanza si riferisce ai valori effettivi che tali dati assumono in un dato momento. Ad esempio, nella relazione DOCENZA, lo schema è costituito dalla struttura fissa che include due attributi: Corso e NomeDocente. Lo schema della relazione si può rappresentare così: DOCENZA(Corso, NomeDocente). Le righe della tabella, invece, sono soggette a cambiamenti nel tempo, riflettendo l'offerta corrente di corsi e i docenti associati. Ad esempio, in un dato momento, l'istanza potrebbe essere: Basi di dati – Rossi Reti – Neri Linguaggi – Verdi In termini tecnici, lo schema rappresenta la componente intensionale della base di dati, mentre l'istanza rappresenta la componente estensionale. 3.0 Architettura a tre livelli delle basi di dati La nozione di modello e di schema può essere ulteriormente articolata attraverso una proposta di architettura a tre livelli per i DBMS. I tre livelli sono: Schema esterno: descrive una porzione della base di dati, specifica per l'utente o un gruppo di utenti. Lo schema esterno può avere un’organizzazione diversa rispetto a quella dello schema logico. In effetti, è possibile avere diversi schemi esterni per uno stesso schema logico. Schema logico: rappresenta una descrizione complessiva della base di dati secondo il modello logico scelto (ad esempio, relazionale o a oggetti). Schema interno: descrive la rappresentazione fisica dei dati, come vengono memorizzati su disco, per esempio tramite file sequenziali o hash. In pratica, nei sistemi moderni, il livello esterno può essere gestito tramite l'uso di viste (views), ovvero relazioni derivate da altre tabelle. Queste permettono di adattare l’accesso ai dati in base alle esigenze degli utenti. Ad esempio, uno studente di Ingegneria Elettronica potrebbe vedere solo i corsi del suo manifesto, tramite una vista sulla base di dati più ampia, come mostrato nella relazione ELETTRONICA. Inoltre, i meccanismi di autorizzazione consentono di regolare l’accesso degli utenti alle informazioni della base di dati, garantendo così sia la sicurezza che la privatezza dei dati sensibili. 4.0 Modelli logici nei sistemi di basi dati Il modello relazionale si fonda su due concetti principali: relazione e tabella. Questi concetti, pur avendo origini diverse, sono strettamente connessi. La relazione proviene dalla teoria degli insiemi nella matematica, mentre la tabella è un concetto intuitivo e di uso comune. Questa combinazione ha contribuito al successo del modello relazionale, in quanto offre sia una base formale rigorosa sia una rappresentazione comprensibile e utilizzabile facilmente dagli utenti finali. 4.1 Indipendenza dei dati Il modello relazionale è stato progettato per garantire l'indipendenza dei dati, separando il livello fisico da quello logico. Gli utenti e i programmatori operano esclusivamente al livello logico, senza la necessità di conoscere come i dati vengono effettivamente memorizzati fisicamente. Questa separazione rende il modello versatile e adatto per molte applicazioni, poiché eventuali modifiche alla struttura fisica dei dati non richiedono cambiamenti nelle applicazioni che li utilizzano. In confronto, i modelli di basi di dati precedenti (come quello reticolare e quello gerarchico) incorporavano riferimenti espliciti alle strutture fisiche attraverso puntatori e l’ordine fisico dei dati. Questo vincolo rendeva più difficile la gestione e l'evoluzione delle basi di dati. 4.1.1 Tre accezioni del termine "relazione" Il termine relazione, nell’ambito delle basi di dati, ha tre diverse interpretazioni: Relazione matematica: deriva dalla teoria degli insiemi e si riferisce a una collezione di n-tuples o coppie ordinate di elementi. Relazione nel modello relazionale: utilizza la nozione matematica come base, ma presenta alcune differenze, come vedremo più avanti. Una tabella rappresenta una relazione in questo contesto. Relazione nel modello Entità-Relazione (Entity-Relationship): questo concetto, tradotto dall'inglese "relationship", è usato per rappresentare i legami tra entità del mondo reale nel modello concettuale. 5.0 Relazioni e Tabelle Il prodotto cartesiano è un concetto fondamentale nella matematica, in particolare nella teoria degli insiemi, ed è alla base del modello relazionale nelle basi di dati. Dati due insiemi 𝐷1 e 𝐷2 , il loro prodotto cartesiano, indicato come 𝐷1 × 𝐷2, è l'insieme di tutte le coppie ordinate (𝑢1 , 𝑢2 ), dove 𝑢1 appartiene a 𝐷1 e 𝑢2 appartiene a 𝐷2. Esempio di prodotto cartesiano: Se consideriamo gli insiemi: 𝐴 = {1,2,4} 𝐵 = {𝑎, 𝑏} Il prodotto cartesiano 𝐴 × 𝐵 è l'insieme di tutte le possibili coppie (𝑎, 𝑏) in cui il primo elemento proviene da A e il secondo da B, cioè: 𝐴 × 𝐵 = {(1, 𝑎), (1, 𝑏), (2, 𝑎), (2, 𝑏), (4, 𝑎), (4, 𝑏)} Questo esempio genera sei coppie ordinate, poiché A contiene 3 elementi e B ne contiene 2, quindi 3 × 2 = 6. 5.0.1 Relazione matematica: Una relazione matematica sugli insiemi 𝐷1 e 𝐷2 è semplicemente un sottoinsieme del prodotto cartesiano 𝐷1 × 𝐷2. Ad esempio, una relazione su A e B potrebbe essere: {(1, 𝑎), (1, 𝑏), (4, 𝑏)} Questa relazione contiene solo alcune coppie del prodotto cartesiano, quindi è un sottoinsieme. 5.0.2 Rappresentazione tabellare: Le relazioni matematiche possono essere rappresentate in forma di tabelle, dove: Le colonne della tabella corrispondono ai domini 𝐷1, 𝐷2 , ecc. Le righe corrispondono alle n-uple o coppie ordinate della relazione. Nell'esempio precedente, il prodotto cartesiano 𝐴 × 𝐵 e la relazione specifica possono essere rappresentati come tabelle con righe e colonne. 5.1 Generalizzazione a n insiemi: Il concetto di prodotto cartesiano può essere generalizzato a più di due insiemi. Dati n insiemi 𝐷1 , 𝐷2 , … , 𝐷𝑛, il loro prodotto cartesiano è indicato come 𝐷1 × 𝐷2 × … × 𝐷𝑛 ed è costituito dalle n-uple (𝑣1 , 𝑣2 , … , 𝑣𝑛 ), dove ciascun 𝑣𝑖 appartiene a 𝐷𝑖. Una relazione matematica su questi insiemi è un sottoinsieme del prodotto cartesiano. Il numero n delle componenti di una n-upla (e quindi del prodotto cartesiano) è chiamato grado della relazione. Il numero di n-uple in una relazione è la sua cardinalità. 5.2 Applicazione pratica nelle basi di dati: Le relazioni matematiche trovano una rappresentazione pratica nel modello relazionale delle basi di dati. Ad esempio, una tabella che registra i risultati di partite di calcio può essere rappresentata come una relazione. Se i domini sono: Squadra1:Stringa Squadra2:Stringa GolSquadra1:Intero GolSquadra2:Intero La relazione risulterebbe essere un sottoinsieme del prodotto cartesiano di questi domini: 𝑆𝑡𝑟𝑖𝑛𝑔𝑎 × 𝑆𝑡𝑟𝑖𝑛𝑔𝑎 × 𝐼𝑛𝑡𝑒𝑟𝑜 × 𝐼𝑛𝑡𝑒𝑟𝑜 Ogni riga della tabella rappresenta una partita e i rispettivi risultati. In sintesi, il concetto di prodotto cartesiano e di relazione formano la base del modello relazionale utilizzato nelle basi di dati, dove ogni tabella rappresenta una relazione matematica che può essere rappresentata e manipolata in modo chiaro e intuitivo. 5.3 Relazioni con attributi Le osservazioni sulle relazioni e le loro rappresentazioni tabellari nel contesto delle basi di dati evidenziano diversi aspetti importanti. Ogni n-upla in una relazione stabilisce un legame tra i suoi elementi. Ad esempio, una n-upla (𝑢1 , 𝑢2 , … , 𝑢𝑛 ) collega i valori dei domini 𝐷1 , 𝐷2 , … , 𝐷𝑛 in un certo ordine. Nella relazione che rappresenta i risultati di partite di calcio, una riga come ("𝐽𝑢𝑣𝑒𝑛𝑡𝑢𝑠", "𝐿𝑎𝑧𝑖𝑜", 3,1) stabilisce un collegamento tra i nomi delle squadre e i rispettivi punteggi. È importante notare che una relazione è un insieme di n-uple, e come tale presenta alcune proprietà. Innanzitutto, non esiste alcun ordinamento tra le n-uple; l'ordine degli elementi in un insieme non è rilevante. Anche se in una rappresentazione tabellare le righe sono visualizzate in un certo ordine, questo è "accidentale". Cambiando l'ordine delle righe, la relazione rappresentata rimane invariata. Inoltre, le n-uple di una relazione devono essere distinte; non possono esserci n-uple duplicate, poiché gli elementi di un insieme devono essere unici. Pertanto, ogni riga di una tabella deve essere unica. Allo stesso tempo, l'ordinamento interno delle n-uple è cruciale. Sebbene l'ordine delle righe in una relazione sia irrilevante, all'interno di ciascuna n-upla l'ordine dei valori è importante. Ogni valore è associato a un dominio specifico e la posizione del valore è fondamentale per interpretarlo correttamente. Se, ad esempio, si scambiasse la posizione dei domini che rappresentano i gol segnati dalle squadre, il significato della relazione cambierebbe drasticamente. Il fatto che i valori siano interpretati in base alla loro posizione rende il concetto di relazione matematica meno flessibile quando si tratta di gestire dati complessi. In molti contesti informatici, si preferisce l'uso di notazioni non posizionali, come nei record, dove i campi sono identificati da nomi (attributi), piuttosto che da posizioni. Questa preferenza deriva dal fatto che i nomi sono più esplicativi e permettono una gestione più semplice e comprensibile dei dati. Quando usiamo le relazioni per organizzare i dati nelle basi di dati, possiamo pensare a ciascuna relazione come a un insieme di record omogenei, definiti sugli stessi campi (o attributi). Ciò rende la struttura della relazione più vicina a quella di un record piuttosto che a una semplice n-upla posizionale. Una relazione che registra i risultati delle partite potrebbe avere attributi come SquadraDiCasa, SquadraOspitata, RetiCasa, e RetiOspitata. Associando a ciascun dominio un nome simbolico (attributo), si può migliorare la chiarezza della relazione. Invece di riferirsi ai domini in base alla loro posizione, possiamo riferirci agli attributi. Per esempio, nella tabella delle partite, gli attributi possono essere usati come intestazioni delle colonne, rendendo la relazione più leggibile e interpretabile. Una volta introdotti gli attributi, l'ordine delle colonne (e degli attributi stessi) diventa irrilevante. Non è più necessario parlare di primo dominio, secondo dominio e così via; è sufficiente fare riferimento agli attributi. Questo consente una maggiore flessibilità nella rappresentazione dei dati. Inoltre, è possibile utilizzare una notazione utile per riferirsi agli attributi, come 𝑡[𝐴], dove t è una tupla e A è un attributo. Questa notazione ci permette di accedere al valore della tupla per un dato attributo. Infine, possiamo formalizzare il concetto di tupla: una tupla su un insieme di attributi X è una funzione t che associa a ciascun attributo 𝐴 ∈ 𝑋 un valore appartenente al dominio associato a A. Con questa nuova definizione, una relazione non è più vista come un insieme di n-uple posizionali, ma come un insieme di tuple che associano i valori ai nomi degli attributi. Questa definizione elimina la necessità di interpretare i dati in base alla loro posizione, rendendo la gestione dei dati più flessibile e intuitiva. 6.0 Relazione e basi di dati Come già osservato, una relazione può essere utilizzata per organizzare dati rilevanti nell'ambito di un'applicazione di interesse. Tuttavia, di solito non è sufficiente una singola relazione; una base di dati è generalmente costituita da più relazioni, le cui tuple contengono valori comuni, necessari per stabilire corrispondenze. Per esemplificare, consideriamo una base di dati in cui: La prima relazione contiene informazioni relative a un insieme di studenti, con numero di matricola, cognome, nome e data di nascita. La terza relazione include informazioni su alcuni corsi, comprendenti codice, titolo e docente. La seconda relazione raccoglie informazioni relative agli esami, specificando il numero di matricola dello studente, il codice del corso e il voto. Questa relazione fa riferimento ai dati contenuti nelle altre due: agli studenti, attraverso i numeri di matricola, e ai corsi, tramite i relativi codici. La base di dati presenta una delle caratteristiche fondamentali del modello relazionale, spesso descritta come "basata su valori": i riferimenti tra dati in relazioni diverse sono rappresentati mediante valori dei domini che compaiono nelle tuple. Gli altri modelli logici, come quello reticolare e gerarchico (sviluppati prima del modello relazionale ma ancora in uso), stabiliscono le corrispondenze in modo esplicito attraverso puntatori e sono perciò detti modelli "basati su record e puntatori". Sebbene non approfondiremo questi modelli, è utile evidenziare la loro caratteristica principale: i dati sono collegati attraverso puntatori. Rispetto a un modello basato su record e puntatori, il modello relazionale presenta diversi vantaggi: Richiede di rappresentare solo ciò che è rilevante dal punto di vista dell'applicazione (e quindi dell'utente); i puntatori sono un elemento aggiuntivo, legato a aspetti realizzativi. Nei modelli con puntatori, il programmatore si riferisce a dati che non sono significativi per l'applicazione. La rappresentazione logica dei dati, costituita esclusivamente dai valori, non fa alcun riferimento alla rappresentazione fisica, che può cambiare nel tempo. Il modello relazionale consente quindi di ottenere l'indipendenza fisica dei dati; poiché tutte le informazioni sono contenute nei valori, è relativamente semplice trasferire i dati da un contesto a un altro (ad esempio, trasferire una base di dati da un computer a un altro). In presenza di puntatori, l'operazione risulta più complessa, poiché i puntatori hanno un significato locale al singolo sistema, il che non sempre è immediato da esportare. È interessante notare che anche in una base di dati relazionale, a livello fisico, i dati possono essere rappresentati attraverso l'uso di puntatori. Tuttavia, nei modelli relazionali, i puntatori non sono visibili a livello logico. Inoltre, nei sistemi di basi di dati a oggetti, che rappresentano una delle direzioni evolutive delle basi di dati, vengono introdotti identificatori di oggetto, i quali, sebbene a un livello di astrazione più alto, presentano alcune caratteristiche dei puntatori. Possiamo ora riassumere le definizioni relative al modello relazionale, distinguendo il livello degli schemi da quello delle istanze: Schema di relazione: costituito da un simbolo R (nome della relazione) e da un insieme di (nomi di) attributi 𝑋 = {𝐴1 , 𝐴2 , … , 𝐴𝑛 }, di solito indicato come 𝑅(𝑋). A ciascun attributo è associato un dominio, come visto in precedenza. Schema di base di dati: un insieme di schemi di relazione con nomi diversi: 𝑅 = {𝑅1 (𝑋1 ), 𝑅2 (𝑋2 ), … , 𝑅𝑛 (𝑋𝑛 )} I nomi di relazione servono principalmente a distinguere le varie relazioni all'interno della base di dati. Istanza di relazione: un insieme r di tuple su X. Istanza di base di dati: un insieme di relazioni 𝑟 = {𝑟1 , 𝑟2 , … , 𝑟𝑛 } su uno schema 𝑅 = {𝑅1 (𝑋1 ), 𝑅2 (𝑋2 ), … , 𝑅𝑛 (𝑋𝑛 )} dove ogni 𝑟𝑖 è una relazione sullo schema 𝑅𝑖 (𝑋𝑖 ). Per chiarire, lo schema della base di dati può essere definito come segue (con opportune definizioni per i domini): 𝑅 = {𝑆𝑇𝑈𝐷𝐸𝑁𝑇𝐼(𝑀𝑎𝑡𝑟𝑖𝑐𝑜𝑙𝑎, 𝐶𝑜𝑔𝑛𝑜𝑚𝑒, 𝑁𝑜𝑚𝑒), 𝐸𝑆𝐴𝑀𝐼(𝑉𝑜𝑡𝑜, 𝐶𝑜𝑟𝑠𝑜), 𝐶𝑂𝑅𝑆𝐼(𝐶𝑜𝑑𝑖𝑐𝑒, 𝑇𝑖𝑡𝑜𝑙𝑜, 𝐷𝑜𝑐𝑒𝑛𝑡𝑒)} Accenniamo brevemente alle convenzioni che adotteremo in seguito, al fine di favorire la sinteticità della notazione senza compromettere la comprensione: Gli attributi verranno indicati con lettere iniziali dell'alfabeto (maiuscole), eventualmente con indici e/o pedici: 𝐴, 𝐵, 𝐶, 𝐴′ , 𝐴1 , … Insiemi di attributi saranno indicati con lettere finali dell'alfabeto (maiuscole): 𝑋, 𝑌, 𝑍, 𝑋′, 𝑋𝑖 , …. Un insieme in cui si vogliano evidenziare gli attributi componenti verrà denotato dalla giustapposizione dei nomi degli attributi stessi: ad esempio, scriveremo 𝑋 = 𝐴𝐵𝐶 anziché 𝑋 = {𝐴, 𝐵, 𝐶}. Analogamente, l'unione di insiemi verrà denotata dalla giustapposizione dei relativi nomi: scriveremo 𝑋𝑌 anziché 𝑋 ∪ 𝑌; unendo le due convenzioni, scriveremo 𝑋𝐴 anziché 𝑋 ∪ {𝐴}. Per i nomi di relazione, quando non si possano o debbano usare nomi significativi, utilizzeremo R e lettere contigue (maiuscole): 𝑅1 , 𝑆, 𝑆′, …. Per le relazioni, utilizzeremo gli stessi simboli, ma in lettere minuscole. Per approfondire ulteriormente i concetti fondamentali del modello relazionale, esaminiamo un paio di esempi. In primo luogo, notiamo che secondo la definizione sono ammissibili relazioni su un solo attributo. Ciò può avere senso, in particolare, in basi di dati composte da più relazioni, in cui la relazione su singolo attributo contiene valori che appaiono come valori di un attributo di un'altra relazione. Ad esempio, in una base di dati che include la relazione STUDENTI, si può utilizzare un'altra relazione sul solo attributo Matricola per indicare gli studenti lavoratori (attraverso i rispettivi numeri di matricola, che devono apparire nella relazione STUDENTI). Discutiamo ora un esempio un po' più complesso, che mostra come, sebbene indirettamente, il modello relazionale consenta di rappresentare informazioni strutturate in modo articolato. Le ricevute fiscali emesse da un ristorante hanno una struttura che prevede, oltre alle frasi prestampate, alcune informazioni fisse (numero, data e totale) e un numero di righe variabile, ognuna relativa a un insieme di portate omogenee (con quantità, descrizione e importo complessivo). Poiché le relazioni hanno una struttura fissa, non è possibile rappresentare l'insieme delle ricevute con un'unica relazione, in quanto non sarebbe possibile gestire le righe in un numero non predeterminato. 7.0 Informazione incompleta e valori nulli La struttura del modello relazionale, come discusso, è molto semplice e potente, ma presenta anche una certa rigidità. Le informazioni devono essere rappresentate tramite tuple di dati omogenee, e ogni relazione può contenere solo tuple conformi al suo schema. Questo può risultare problematico quando i dati disponibili non corrispondono esattamente al formato previsto. Ad esempio, in una relazione con lo schema: PERSONE(Cognome, Nome, Indirizzo, Telefono) il valore dell'attributo Telefono potrebbe non essere disponibile per tutte le tuple. Utilizzare un valore del dominio per rappresentare l'assenza di informazioni, come ad esempio lo zero per i numeri di telefono, può generare confusione e non è una pratica corretta. Ciò richiede che esista un valore mai utilizzato per indicare l'assenza, e non sempre è possibile trovare un valore del dominio adeguato. Ad esempio, in un attributo per le date di nascita, non ci sono valori non utilizzati in un tipo Data correttamente definito. Inoltre, l'uso di valori del dominio può mascherare la distinzione tra valori "veri" e valori fittizi, rendendo necessario che i programmi accedano alla base di dati con attenzione, distinguendo correttamente i valori. Per rappresentare la non disponibilità di valori in modo più chiaro, si introduce il concetto di valore nullo. Un valore nullo denota l'assenza di informazione ed è un valore speciale distinto dai valori del dominio. Nelle rappresentazioni tabellari, il valore nullo viene indicato con il simbolo NULL. Consideriamo i seguenti esempi di valori nulli: Città A: ha un valore nullo per l'indirizzo della prefettura, indicando che il valore è sconosciuto. Città B: non ha una prefettura, quindi l'attributo relativo è inesistente; il valore nullo rappresenta l'inesistenza del valore. Città C: è una nuova provincia e non si conosce né se esista né quale sia l'indirizzo della prefettura; il valore nullo indica l'incertezza riguardo all'esistenza dell'informazione. Nei sistemi di basi di dati relazionali, i valori nulli sono gestiti in modo semplice, senza ipotesi particolari, collocandoli nella categoria di valori "senza informazione". Riflettendo ulteriormente sui valori nulli, consideriamo una base di dati con lo stesso schema delle PERSONE. Un valore nullo per la data di nascita potrebbe essere accettabile, mentre un valore nullo per il numero di matricola o per il codice di un corso comporterebbe problematiche maggiori, poiché questi valori sono essenziali per stabilire correlazioni tra tuple di relazioni diverse. La presenza di valori nulli in una relazione, come nella relazione degli esami, può rendere inutilizzabili le informazioni. Inoltre, un numero eccessivo di valori nulli in una relazione può sollevare dubbi sull'identità delle tuple stesse. È quindi cruciale controllare la presenza di valori nulli nelle relazioni, specificando che sono ammessi solo su alcuni attributi e non su altri. Alla fine, vedremo che è possibile definire criteri per individuare quali attributi non dovrebbero contenere valori nulli. 8.0 Vincoli di Integrità In una base di dati, è fondamentale evitare situazioni problematiche come quelle descritte in precedenza. A tal fine, è stato introdotto il concetto di vincolo di integrità, che rappresenta una proprietà che deve essere soddisfatta dalle istanze per garantire informazioni corrette per l'applicazione. Ogni vincolo può essere considerato come un predicato che associa a ogni istanza un valore di vero o falso. Se il predicato assume il valore vero, si afferma che l'istanza soddisfa il vincolo. In generale, a uno schema di base di dati è associato un insieme di vincoli e si considerano corrette (o lecite, o ammissibili) le istanze che soddisfano tutti i vincoli. Per ciascuno dei casi precedentemente discussi, potrebbe essere introdotto un vincolo per vietare la situazione indesiderata. I vincoli possono essere classificati in base agli elementi della base di dati che ne sono coinvolti. Distinguiamo due categorie, di cui la prima presenta alcuni casi particolari: Vincoli intrarelazionali: questi vincoli sono definiti rispetto a singole relazioni della base di dati. I primi tre casi precedentemente discussi corrispondono a vincoli intrarelazionali. Talvolta, il coinvolgimento riguarda le tuple (o addirittura i valori) separatamente l'una dall'altra: Vincolo di tupla: è un vincolo che può essere valutato su ciascuna tupla indipendentemente dalle altre. I vincoli relativi ai primi due casi rientrano in questa categoria. Vincolo su valori o vincolo di dominio: è un caso ancora più specifico, in cui si impone una restrizione sul dominio di un attributo. Ad esempio, nel caso in cui sono ammessi solo valori dell'attributo Voto compresi fra 18 e 30. Vincoli interrelazionali: questi vincoli coinvolgono più relazioni. Ad esempio, nella situazione indesiderata del quarto caso, è possibile vietare la situazione richiedendo che un numero di matricola compaia nella relazione ESAMI solo se è presente nella relazione STUDENTI. Nei prossimi paragrafi, esamineremo con maggiore dettaglio tre classi di vincoli molto importanti: Una classe interessante di vincoli di tupla. I vincoli di chiave, che sono i più rilevanti vincoli intrarelazionali. I vincoli di integrità referenziale, che sono i vincoli interrelazionali di maggiore interesse. 8.1 Vincoli di tupla Come abbiamo discusso, i vincoli di tupla esprimono condizioni sui valori di ciascuna tupla, indipendentemente dalle altre. Una sintassi utile per esprimere vincoli di questo tipo consente di definire espressioni booleane, che utilizzano connettivi logici come AND, OR e NOT, con atomi che confrontano (con operatori di uguaglianza, disuguaglianza e ordinamento) valori di attributo o espressioni aritmetiche su valori di attributo. I vincoli violati identificati possono essere descritti come segue: (𝑉𝑜𝑡𝑜 > 18) 𝐴𝑁𝐷 (𝑉𝑜𝑡𝑜 < 30) 𝑁𝑂𝑇 (𝐿𝑜𝑑𝑒 = ′𝑙𝑜𝑑𝑒′) 𝑂𝑅 (𝑉𝑜𝑡𝑜 = 30) In particolare, il secondo vincolo stabilisce che la lode è ammissibile solo se il voto è pari a 30, affermando che o non c'è la lode, oppure il voto è pari a 30. Il primo vincolo è, infatti, un vincolo di dominio, in quanto coinvolge un solo attributo. La definizione di vincolo di tupla può includere anche espressioni più complesse, purché siano definite sui valori delle singole tuple. Ad esempio, su una relazione con lo schema: 𝑷𝑨𝑮𝑨𝑴𝑬𝑵𝑻𝑰(𝑫𝒂𝒕𝒂, 𝑰𝒎𝒑𝒐𝒓𝒕𝒐, 𝑹𝒊𝒕𝒆𝒏𝒖𝒕𝒆, 𝑵𝒆𝒕𝒕𝒐) è possibile definire il vincolo che richiede, come naturale, che il netto sia pari alla differenza tra l'importo originario e le ritenute, nel modo seguente: 𝑁𝑒𝑡𝑡𝑜 = 𝐼𝑚𝑝𝑜𝑟𝑡𝑜 − 𝑅𝑖𝑡𝑒𝑛𝑢𝑡𝑒 Questo tipo di vincolo garantisce che le relazioni contabili siano mantenute in modo corretto, evitando errori nei dati inseriti. 9.0 Chiavi In questo paragrafo discutiamo i vincoli di chiave, che sono fondamentali nel modello relazionale; senza di essi, il modello stesso risulterebbe privo di significato. Cominciamo con un esempio pratico. Nella relazione in esame, i valori dell'attributo Matricola sono tutti univoci; ciò significa che la matricola identifica univocamente gli studenti. Questo concetto è stato introdotto nelle università per consentire un riferimento chiaro e non ambiguo agli studenti. Analogamente, i dati anagrafici, come Cognome, Nome e Nascita, possono anch'essi identificare univocamente le persone. Intuitivamente, una chiave è un insieme di attributi usato per identificare univocamente le tuple di una relazione. La definizione formale di chiave può essere espressa in due passi: Un insieme K di attributi è considerato una superchiave di una relazione r se non esistono due tuple distinte 𝑡1 e 𝑡2 in r tali che 𝑡1 [𝐾] = 𝑡2 [𝐾]. K è una chiave di r se è una superchiave minimale, ovvero non esiste un'altra superchiave K' di r che sia un sottoinsieme proprio di K. Prendiamo in considerazione i seguenti esempi: L'insieme {Matricola} è una superchiave e, poiché non contiene sottoinsiemi che siano anch'essi superchiavi, è anche una chiave. L'insieme {Cognome, Nome, Nascita} è anch'esso una superchiave e, non avendo sottoinsiemi che siano superchiavi, è un'altra chiave. L'insieme {Matricola, Corso} è una superchiave, ma non è una chiave minimale, poiché {Matricola} è già una superchiave. L'insieme {Nome, Corso} non è una superchiave, poiché esistono tuple che condividono gli stessi valori su entrambi gli attributi. Sebbene {Cognome, Corso} possa essere una chiave in questo specifico caso, non possiamo affermare che sia così in generale, poiché potrebbero esserci studenti con lo stesso cognome iscritti allo stesso corso. Quando definiamo uno schema, associamo a esso vincoli di integrità che si applicano a tutte le istanze valide di quello schema. In particolare, per lo schema: STUDENTI(Matricola, Cognome, Nome, Nascita, Corso) si devono considerare come chiavi gli insiemi: {Matricola} {Cognome, Nome, Nascita} Entrambe le relazioni di esempio soddisfano questi vincoli. Le riflessioni sul concetto di chiave ci portano a evidenziare che ogni relazione e ogni schema di relazione devono avere almeno una chiave. Poiché ogni relazione è un insieme, è garantito che per ogni relazione r(X), l'insieme X di tutti gli attributi sia una superchiave. Se tale insieme non è chiave, possiamo ricercare sottoinsiemi fino a trovare una superchiave minimale. Questo assicura che ogni relazione abbia una chiave e, di conseguenza, che esista almeno una chiave per ogni schema. La presenza di chiavi garantisce che i valori siano univocamente identificabili e facilita le corrispondenze tra dati di relazioni diverse. Ad esempio, nella relazione ESAMI, gli studenti sono identificati tramite i numeri di matricola, che fungono da chiave nella relazione STUDENTI, e i corsi sono identificati tramite i relativi codici, che rappresentano la chiave nella relazione CORSI. 9.1 Chiavi e valori Nulli La presenza di valori nulli nelle relazioni può compromettere l'identificazione univoca delle tuple. Quando ci sono valori nulli nelle chiavi, diventa difficile distinguere tra tuple e stabilire riferimenti tra relazioni diverse. Ad esempio, se una tupla ha valori nulli per gli attributi che compongono la chiave, non può essere identificata e non possiamo sapere se un nuovo record si riferisce alla stessa entità. Per affrontare questo problema, è fondamentale limitare i valori nulli nelle chiavi. In particolare, si vieta la presenza di valori nulli nella chiave primaria, garantendo così che ogni tupla possa essere identificata univocamente. In altre chiavi, invece, i valori nulli possono essere ammessi, a meno di esigenze specifiche. Gli attributi della chiave primaria sono spesso evidenziati per indicarne l'importanza. In situazioni in cui non ci sono attributi sempre disponibili e identificativi, è possibile introdurre un attributo aggiuntivo, come un codice generato, per garantire l'univocità delle tuple. Questo approccio è comune in vari contesti, in cui codici identificativi (come numero di matricola o codice fiscale) sono utilizzati per assicurare un’identificazione chiara e per facilitare i riferimenti tra le entità. 10.0 Vincoli di integrità referenziale I vincoli di integrità referenziale sono fondamentali per garantire la coerenza e l'integrità dei dati all'interno di un sistema di gestione di basi di dati relazionali. Questi vincoli stabiliscono e mantengono relazioni significative tra diverse tabelle, impedendo la creazione di collegamenti inconsistenti e migliorando la comprensibilità del sistema nel suo complesso. 10.1 Importanza dei Vincoli di Integrità Referenziale Coerenza dei Dati: I vincoli di integrità referenziale assicurano che ogni riferimento a un valore in un'altra tabella sia valido. Ad esempio, se una tabella delle infrazioni fa riferimento a un agente tramite un numero di matricola, questo numero deve esistere nella tabella degli agenti. Ciò evita situazioni in cui un'infrazione si riferisce a un agente inesistente, riducendo il rischio di confusione e errori nei rapporti. Prevenzione dei Dati Orfani: Questi vincoli impediscono la creazione di record "orfani", ossia record in una tabella che non possono essere collegati a record in un'altra tabella. Ad esempio, se un agente viene rimosso dalla tabella degli agenti, tutte le infrazioni associate devono essere anch'esse rimosse o aggiornate. Senza i vincoli, si rischierebbe di avere infrazioni che non possono più essere collegate a nessun agente. Chiarezza e Comprensibilità: I vincoli di integrità referenziale forniscono una struttura chiara e comprensibile per i dati. Quando i riferimenti sono ben definiti, è più facile per gli sviluppatori e gli utenti capire come le tabelle si collegano tra loro, facilitando la manutenzione e l'aggiornamento della base di dati. Operazioni di Manutenzione: Durante la manutenzione dei dati, come l'aggiornamento o l'eliminazione di record, i vincoli di integrità referenziale forniscono linee guida su come procedere. Se un agente viene trasferito o rimosso, il sistema può impedire automaticamente l'eliminazione se ci sono infrazioni associate, avvisando l'utente della presenza di riferimenti attivi. 10.2 Definizione e Implementazione dei Vincoli Un vincolo di integrità referenziale viene definito tra un insieme di attributi in una tabella e una chiave primaria in un'altra. Affinché questo vincolo sia soddisfatto, ogni valore dell'insieme di attributi deve corrispondere a un valore della chiave primaria nell'altra tabella. Chiave Primaria e Chiave Esterna: La chiave primaria identifica in modo univoco ogni record in una tabella, mentre la chiave esterna stabilisce un legame con la chiave primaria di un'altra tabella. È fondamentale che i valori nella chiave esterna corrispondano a quelli nella chiave primaria per mantenere l'integrità referenziale. Ordine degli Attributi: Quando le chiavi primarie sono composte da più attributi, è essenziale rispettare l'ordine di questi attributi. Se una chiave primaria è composta da "Prov" e "Numero", i valori di riferimento devono seguire questo ordine per garantire che la corrispondenza sia valida. 11.0 Modello Entità - Relazione (ER) Nell'ambito della progettazione dei database, il modello entità-relazione (o modello entità-associazione; più comunemente modello E-R) è un modello teorico per la rappresentazione concettuale e grafica dei dati a un alto livello di astrazione, formalizzato da Peter Chen nel 1976. Il modello entità-relazione viene spesso utilizzato nella prima fase della progettazione di una base di dati, in cui è necessario tradurre le informazioni risultanti dall'analisi di un determinato dominio in uno schema concettuale, detto diagramma entità-relazione (o diagramma E-R). Nella progettazione ingegneristica delle basi di dati si distinguono tre livelli indipendenti e consecutivi di progettazione: progettazione concettuale, progettazione logica e progettazione fisica. Il modello E-R è la tecnica principale per la fase di progettazione concettuale, mentre il modello relazionale è utilizzato per la progettazione logica. Solo nella fase di progettazione fisica si prendono in considerazione software e hardware applicativi, proprietari e non, presenti sul mercato. 11.1 Generalità Il modello E-R si basa su un insieme di concetti molto vicini alla realtà di interesse: quindi facilmente intuibili dai progettisti (e in genere considerati sufficientemente comprensibili e significativi anche per i non-tecnici), ma non implementabili sugli elaboratori. Infatti, pur essendo orientato alla progettazione di basi di dati, il modello prescinde dai criteri specifici di organizzazione fisica dei dati persistenti nei sistemi informatici. Esistono tecniche per la traduzione dei concetti ad alto livello (meglio comprensibili per gli umani) in concetti di più basso livello tipici dei vari modelli logici (ad esempio il modello relazionale) implementati nei diversi DBMS esistenti. Il modello E-R ha rappresentato per lungo tempo (e forse ancora oggi) uno degli approcci più solidi per la modellazione di domini applicativi in ambito informatico; per questo motivo, è stato spesso usato anche al di fuori del contesto della progettazione di database, ed è stato utilizzato come modello di riferimento per numerose altre notazioni per la modellazione. Al modello E-R era ispirata, tra l'altro, la notazione OMT poi confluita in UML. Tramite una superchiave identificativa (campi: ID_codice padre, ID_codice figlio), lo schema Entità-Associazione rappresenta un grafo ad albero su un numero di livelli a piacere (in particolare anche una distinta base), assai diffusa nel mondo informatico. 11.2 I Costrutti principali del modello Entità Le entità rappresentano classi di oggetti, fatti, cose o persone che condividono proprietà comuni e hanno un'esistenza autonoma nell'applicazione di interesse. Un'occorrenza di un'entità è un'istanza della classe rappresentata dall'entità. Un'occorrenza di entità ha un'esistenza indipendente dalle sue proprietà, il che differenzia il modello E-R dal modello relazionale, dove non è possibile rappresentare un oggetto senza conoscere alcune sue proprietà. In uno schema, ogni entità ha un nome univoco e viene rappresentata graficamente con un rettangolo contenente il nome dell'entità. Associazione Le associazioni (o relazioni) rappresentano un legame tra due o più entità. Il numero di entità coinvolte è definito dal grado dell'associazione, con una prevalenza di associazioni di grado due nei buoni schemi E-R. È possibile creare associazioni tra un'entità e se stessa (associazione ad anello) e collegare le stesse entità con più associazioni. Graficamente, l'associazione è rappresentata da un rombo contenente il nome dell'associazione, che può essere un verbo o un sostantivo. Attributi Le entità e le associazioni possono essere descritte tramite attributi, che rappresentano le proprietà condivise dagli oggetti della stessa classe entità o associazione. Gli attributi riflettono il livello di dettaglio con cui si vogliono rappresentare le informazioni. Per ogni classe di entità o associazione, si definisce una chiave, ovvero un insieme minimale di attributi che identificano univocamente un'istanza. Gli attributi sono rappresentati graficamente da ellissi, e la chiave primaria viene indicata sottolineando o cerchiando il nome dell'attributo. 11.3 Tipi di Attributi di un Entità attributi semplici: sono descritti per mezzo di linee terminate da cerchi e da nomi; identificatori delle entità: sono usati come “strumento” per l’identificazione univoca delle occorrenze dell’entità, vengono indicati con cerchietto pieno; attributi composti: sono identificati tramite i loro attributi componenti; attributi multivalore: sono costituiti da un insieme di valori ed indicati tramite due numeri che esprimono la cardinalità dell’attributo; attributi opzionali: cioè con cardinalità(0, 1); sono usati quando non è necessario specificare un valore dell’attributo in quanto la sua assenza non compromette la significatività del concetto. Attributo Composto È l’insieme di attributi di una medesima entità o relazione che presentano affinità nel loro significato o uso. Esempio: Via, Numero civico e CAP formano un Indirizzo Cardinalità delle associazioni Le cardinalità delle associazioni indicano, per ciascuna entità che partecipa a un'associazione, quante volte un'occorrenza di una di queste entità può essere legata a occorrenze delle altre entità coinvolte. Specificano il minimo e il massimo di occorrenze per ciascuna entità nell'associazione. Cardinalità degli attributi È possibile definire vincoli di cardinalità anche sugli attributi per indicare opzionalità o attributi multivalore. Se non viene specificato alcun vincolo, la cardinalità dell'attributo è generalmente (1,1). Ad esempio: (0,1) NumeroPatente: un impiegato può avere una patente o nessuna. (0,n) NumeroTelefono: un impiegato può avere molti numeri di telefono o nessuno. (1,n) TitoloStudio: un impiegato deve avere almeno un titolo di studio, ma può averne molti. Identificatori delle entità Gli identificatori sono un sottoinsieme di attributi che identificano univocamente ogni occorrenza di un'entità. Ad esempio, l'attributo CodiceFiscale identifica univocamente ogni cittadino nell'entità CittadinoItaliano, in quanto non possono esistere due cittadini con lo stesso codice fiscale. Identificatori esterni e interni Gli identificatori interni sono attributi che appartengono all'entità stessa e che consentono di identificarla univocamente. Questi sono gli attributi che compongono la chiave primaria di un'entità. Esempi: Se consideriamo l'entità Studente, un identificatore interno potrebbe essere il numero di matricola: ogni studente ha un numero di matricola unico che lo distingue da tutti gli altri studenti. Per l'entità Prodotto, potrebbe essere il codice prodotto, univoco per ogni prodotto. Gli identificatori esterni sono attributi che fanno riferimento a un'altra entità e vengono utilizzati per rappresentare le relazioni tra diverse entità. Questi attributi fungono da chiavi esterne (foreign key) e sono utilizzati per stabilire legami tra entità diverse. Esempi: Se abbiamo un'entità Ordine e un'entità Cliente, un identificatore esterno nell'entità Ordine potrebbe essere il codice cliente: questa è una chiave esterna che fa riferimento al cliente che ha effettuato l'ordine. Se consideriamo un database per un'azienda, l'entità Dipendente potrebbe avere un attributo codice dipartimento, che è un identificatore esterno e rappresenta il legame con l'entità Dipartimento. 11.4 Generalizzazioni Le generalizzazioni rappresentano legami logici tra due o più entità, distinguendo: Entità padre: una sola. Entità figlie: una o più, che rappresentano "casi particolari" dell'entità padre. Le entità figlie ereditano tutti gli attributi dell'entità padre, ma possono avere anche attributi propri che le distinguono. Ad esempio, l'entità padre "Persona" ha attributi come codice fiscale, cognome, nome e età, mentre le entità figlie "Uomo" e "Donna" possono avere attributi specifici. Le generalizzazioni possono essere: Totali: se l'unione dei sottoinsiemi delle entità figlie costituisce l'insieme dell'entità padre (es. la generalizzazione da "Persona" a "Uomo" o "Donna"). Parziali: se l'unione dei sottoinsiemi delle entità figlie non identifica l'insieme padre (es. "MezzoDiLocomozione" con "Bicicletta" e "Automobile"). Inoltre, le generalizzazioni possono essere: Esclusive: se l'intersezione tra i sottoinsiemi delle entità figlie è vuota. Sovrapposte: se l'intersezione non è vuota (es. un "Lavoratore" può essere sia "Impiegato" che "Studente"). 12.0 Progettazione Logica del modello ER La progettazione logica rappresenta un passaggio cruciale nel processo di sviluppo di un database, poiché consente di tradurre lo schema concettuale, solitamente rappresentato attraverso un modello Entità-Relazione (ER), in uno schema logico di dati. Questo schema logico si fonda su un modello specifico di rappresentazione dei dati, come il modello relazionale, e fornisce una struttura che è concreta e indipendente dai dettagli fisici dell'implementazione. 12.1 Traduzione dello Schema Concettuale La fase di traduzione è fondamentale perché permette di trasformare le entità e le relazioni definite nel modello concettuale in tabelle, colonne e relazioni nel modello logico. Tuttavia, non tutte le caratteristiche del modello ER possono essere tradotte direttamente in un modello logico. Per esempio, gli attributi composti, che possono contenere più valori all'interno di un singolo attributo, e gli attributi multivalore, che consentono di memorizzare più istanze di un attributo, non possono essere direttamente rappresentati nel modello relazionale. Pertanto, è necessario un processo di semplificazione dello schema concettuale. 12.1.1 Semplificazione dello Schema Concettuale La semplificazione è necessaria per ottenere uno schema che possa essere facilmente tradotto e ottimizzato. Durante questo processo, gli attributi composti e multivalore devono essere trasformati in attributi semplici. Ad esempio, un attributo "Indirizzo" che contiene "Via", "Città" e "CAP" può essere scomposto in attributi distinti all'interno di una tabella. Allo stesso modo, gli attributi multivalore devono essere gestiti creando tabelle separate e definendo relazioni tra le entità. Questo processo di semplificazione non solo rende più facile la traduzione, ma contribuisce anche a ottenere uno schema logico più ottimizzato. Un modello ottimizzato garantisce che i dati siano organizzati in modo da ridurre la ridondanza e migliorare l'efficienza delle operazioni di accesso e manipolazione dei dati. 12.1.2 Schema Logico Finale Una volta completate le fasi di trasformazione e traduzione, il risultato finale è lo schema logico, che nel caso del modello relazionale si traduce in un insieme di tabelle da istanziare. Ogni tabella rappresenta un'entità, e le colonne all'interno delle tabelle rappresentano gli attributi di queste entità. È fondamentale definire le chiavi primarie per ogni tabella, che serviranno per identificare univocamente ciascuna riga, e le chiavi esterne, che stabiliranno relazioni tra le tabelle. In aggiunta alla creazione dello schema logico, è essenziale considerare la normalizzazione. Questo processo consiste nell'analizzare le tabelle per eliminare la ridondanza e assicurarsi che i dati siano memorizzati in modo coerente. La normalizzazione si basa su una serie di forme normali, ciascuna delle quali fornisce linee guida per strutturare le tabelle e le relazioni in modo efficiente. 12.1.3 Vincoli di Integrità Oltre alla struttura delle tabelle, è necessario definire i vincoli di integrità. Questi vincoli garantiscono la coerenza e l'affidabilità dei dati nel database. I vincoli di integrità possono includere vincoli di chiave primaria, vincoli di chiave esterna, vincoli di unicità e vincoli di controllo sui valori degli attributi. È importante definire questi vincoli all'interno dello schema logico, ma anche includerli nei programmi di gestione del database per garantire che le regole siano rispettate durante le operazioni di inserimento, aggiornamento e cancellazione dei dati. 12.2 Trasformazione dallo Schema E-R nel modello Relazionale La trasformazione dello schema concettuale in uno schema logico è una fase cruciale nel processo di progettazione di un database. Essa consiste in vari passaggi, tra cui l'eliminazione di attributi multivalore e composti, la scelta e l'aggiunta di identificatori primari. Queste operazioni mirano a garantire che il modello logico soddisfi i requisiti del modello relazionale e sia ottimizzato per l'accesso ai dati. 12.2.0 Eliminazione di Attributi Multivalore e Composti Un primo passo nella trasformazione è l'eliminazione di attributi che non possono essere direttamente tradotti nello schema logico. Gli attributi composti e multivalore non rispettano la prima forma normale (1NF), la quale richiede che ogni attributo contenga solo valori atomici e univoci. Attributi Compositi: Un attributo composto è un attributo che può essere suddiviso in più attributi semplici. Per esempio, un attributo "Indirizzo" che include "Via", "Città" e "CAP" deve essere scomposto in tre attributi distinti: "Via", "Città", e "CAP". Questo processo è reiterabile, nel senso che ogni attributo composto può essere ulteriormente scomposto in attributi semplici, se necessario. Attributi Multivalore: Un attributo multivalore è un attributo che può contenere più valori per una singola istanza dell'entità. Per esempio, se un'entità "Studente" ha un attributo "Lingue Parlate" che può includere più lingue, si deve introdurre una nuova entità, ad esempio "Lingua", e creare una relazione uno a molti tra "Studente" e "Lingua". Questa relazione consente di gestire i dati in modo più efficace, evitando la duplicazione e garantendo una rappresentazione chiara delle informazioni. 12.2.1 Scelta e Aggiunta degli Identificatori Primari Una volta eliminati gli attributi multivalore e composti, il passo successivo è la scelta dell'identificatore principale per ogni entità. L'identificatore principale, o chiave primaria, è essenziale perché su di esso si basano le relazioni tra le entità e perché i sistemi di gestione di database (DBMS) utilizzano le chiavi primarie per creare indici di accesso ai dati. Criteri di Scelta degli Identificatori Primari: Rispetto del Principio di Unicità: Un identificatore deve garantire che ogni istanza dell'entità possa essere univocamente identificata. Non possono essere scelti identificatori basati su attributi che ammettono valori nulli, poiché questi non garantiscono l'accesso a tutte le occorrenze dell’entità. Criteri di Efficienza: Si preferisce utilizzare identificatori composti da un minor numero di attributi. Questo perché i DBMS generano indici più piccoli e gestibili per chiavi primarie con meno attributi, il che porta a un’occupazione ridotta di spazio e a un accesso più efficiente. Inoltre, si prediligono identificatori utilizzati da un maggior numero di operazioni, in modo che possano sfruttare gli indici già creati dal DBMS. 12.3 Traduzione dello Schema E-R nel Modello Relazionale Al termine della fase di trasformazione, il progettista dispone di uno schema Entità-Relazione (E-R) semplificato e adattato alle esigenze del modello logico. Questo schema E-R costituisce la base per la traduzione nel modello relazionale, da cui verrà generato lo schema della base di dati. Questa traduzione è fondamentale perché consente di creare una rappresentazione strutturata dei dati che può essere implementata efficacemente in un sistema di gestione di database (DBMS). 12.3.0 Risultati della Traduzione Durante la traduzione nel modello relazionale, si esegue una trasformazione significativa: Entità in Tabelle: Ogni entità presente nel modello E-R viene tradotta in una tabella nel modello relazionale. Il nome della tabella corrisponde al nome dell'entità, ma viene generalmente utilizzato il plurale per riflettere che la tabella contiene molte istanze di quell'entità. Associazioni in Tabelle: Le associazioni tra entità, che rappresentano relazioni, vengono anch'esse tradotte in tabelle. Queste tabelle servono a connettere le entità coinvolte e a gestire le informazioni relative alle relazioni tra di esse. Vincoli di Integrità Referenziali: Tra le tabelle di associazioni e quelle delle entità collegate vengono stabiliti vincoli di integrità referenziale. Questi vincoli garantiscono che i dati rimangano coerenti, assicurando che un identificatore di un'entità possa essere trovato nella tabella collegata. In pratica, le tabelle delle relazioni includono gli identificatori di tutte le entità coinvolte. 12.4 Problematiche di Traduzione a Seconda della Cardinalità delle Associazioni La traduzione delle associazioni presenta diverse problematiche in base alla cardinalità delle relazioni, che possono essere classificate come: Uno a Uno (1:1): In questo tipo di relazione, ogni istanza di un'entità è collegata a una sola istanza di un'altra entità. In questo caso, si può scegliere di mantenere i dati in una sola tabella oppure in due tabelle separate, a seconda delle esigenze di progettazione. Uno a Molti (1:M): Qui, un'entità può essere collegata a più istanze di un'altra entità, ma non viceversa. In questo caso, l'identificatore della tabella dell'entità "uno" viene inserito come chiave esterna nella tabella dell'entità "molti". Molti a Molti (M:M): Le associazioni molti a molti richiedono una gestione più complessa. In questo scenario, si deve creare una nuova tabella di associazione che contenga gli identificatori di entrambe le entità coinvolte, formando così una chiave composta. Questa tabella di associazione può anche contenere attributi specifici della relazione. Traduzione delle Entità Quando si traduce un'entità in una tabella, il risultato finale avrà le seguenti caratteristiche: Nome della Tabella: Il nome della tabella sarà il nome dell’entità al plurale, per rappresentare le molteplici istanze. Attributi: Gli attributi dell’entità diventeranno le colonne della tabella, mantenendo i nomi originali. Chiave Primaria: L’identificatore principale dell’entità viene definito come chiave primaria della tabella, garantendo l'unicità di ogni riga. Traduzione delle Associazioni Molti a Molti La traduzione di un’associazione molti a molti produce una tabella che ha: Nome della Tabella: Il nome della tabella sarà il nome dell’associazione al plurale. Attributi: Includerà gli attributi della relazione, se presenti, e gli identificatori di tutte le entità coinvolte. Questi identificatori formeranno la chiave della relazione. In alcuni casi, possono formare una superchiave, da cui è necessario scegliere un sottoinsieme appropriato per la chiave primaria. Ridenominazione per Leggibilità: Per migliorare la leggibilità degli schemi, può essere utile ridenominare gli attributi degli identificatori e rendere espliciti i nomi delle chiavi esterne. Esempio Ovvero: IMPIEGATO(Matricola, Cognome, Stipendio) PROGETTI(Codice, Nome, Budget) PARTECIPAZIONE(Matricola, Codice, Data Inizio) Per migliorare la comprensione della tabella PARTECIPAZIONI conviene rinominare gli attributi presi dalle entità ed esplicitare le chiavi esterne: PARTECIPAZIONI(Impiegato:IMPIEGATI, Progetto:PROGETTI, Data Inizio) VINCOLI DI INTEGRITÀ REFERENZIALI TRA DI E DI Impiegato PARTECIPAZIONI Matricola IMPIEGATI Progetto PARTECIPAZIONI Codice PROGETTI 13.0 Vincoli delle Gerarchie Le gerarchie nelle basi di dati possono essere classificate utilizzando due tipi fondamentali di vincoli: i vincoli di copertura e i vincoli di disgiunzione, che regolano il modo in cui le occorrenze delle sottoclassi si relazionano con quelle della superclasse. I vincoli di copertura riguardano la distinzione tra generalizzazione totale o parziale. Una generalizzazione è totale quando ogni occorrenza della superclasse (classe padre) appartiene almeno a una delle sottoclassi (entità figlie). In altre parole, non esistono istanze che appartengano solo alla superclasse senza essere classificate in una sottoclasse. Al contrario, la generalizzazione è considerata parziale quando alcune occorrenze della superclasse non appartengono a nessuna delle sottoclassi, restando quindi escluse dalla suddivisione. I vincoli di disgiunzione, invece, determinano se una stessa occorrenza della superclasse può appartenere a più sottoclassi contemporaneamente. Se la generalizzazione è disgiunta, significa che ogni occorrenza della superclasse può essere parte di una sola sottoclasse; in altre parole, le sottoclassi non possono sovrapporsi. Se la generalizzazione è invece sovrapposta, un’istanza della superclasse può appartenere a più di una sottoclasse simultaneamente, permettendo quindi che le sottoclassi condividano alcune occorrenze. Totale e Disgiunta Parziale e Disgiunta Parziale e Sovrapposta Da sovrapposta ad esclusiva Le generalizzazioni sovrapposte possono essere trasformate in generalizzazioni esclusive introducendo una o più entità figlie che rappresentano le sovrapposizioni tra le sottoclassi. In una generalizzazione sovrapposta, un'istanza della superclasse può appartenere a più sottoclassi contemporaneamente, creando potenziali ambiguità. Per eliminare queste sovrapposizioni e rendere la gerarchia esclusiva, si possono aggiungere nuove entità figlie che catturano esplicitamente queste combinazioni. In questo modo, ogni istanza della superclasse sarà associata solo a una sottoclasse, garantendo che le generalizzazioni siano disgiunte e mantenendo una rappresentazione più chiara e strutturata dei dati. È sufficiente aggiungere l’entità StudenteLavoratore per ottenere una generalizzazione esclusiva da quella con i soli Studente e Lavoratore. 13.1 Risoluzione delle Gerarchie Le gerarchie di generalizzazione sono strumenti potenti nella progettazione concettuale dei database, utilizzati per rappresentare in modo chiaro le relazioni e le dipendenze tra le diverse entità presenti nel mondo reale. Tuttavia, queste gerarchie non trovano una corrispondenza diretta nei modelli logici delle basi di dati, poiché non esiste un costrutto logico equivalente che le rappresenti immediatamente. Per implementarle, è necessario trasformare il costrutto concettuale della gerarchia in una combinazione di costrutti di base, come entità e relazioni. Questa trasformazione permette di mantenere la struttura delle informazioni attraverso la creazione di tabelle che rappresentano le diverse sottoclassi, insieme alle relazioni che le collegano alla superclasse, rispettando i vincoli di integrità e garantendo una gestione efficace dei dati. Tre alternative Il costrutto di generalizzazione può essere trasformato in tre modi principali durante la progettazione logica di un database. Il primo approccio è l'accorpamento nell'entità padre delle entità figlie, dove la generalizzazione assorbe le specializzazioni, combinando in un'unica tabella tutti gli attributi dell'entità padre e delle sue sottoclassi. Il secondo metodo consiste nell'accorpamento dell'entità padre nelle entità figlie, in cui ogni sottoclasse eredita direttamente gli attributi della superclasse, creando tabelle separate per ciascuna entità figlia con tutte le informazioni necessarie. Infine, la terza opzione è la sostituzione della generalizzazione con relazioni, dove la generalizzazione viene tradotta in una serie di relazioni uno a uno che collegano l'entità padre a ciascuna entità figlia, mantenendo così una separazione ma legando strettamente i dati tra padre e figlie. Ogni approccio ha vantaggi e svantaggi in termini di efficienza e semplicità di gestione. Prima Soluzione La terza modalità di trasformazione, identificata come trasformazione c) del costrutto di generalizzazione, ossia la sostituzione della generalizzazione con relazioni, è solitamente applicata in presenza di generalizzazioni parziali. Questo approccio è particolarmente vantaggioso quando ci sono operazioni distinte che devono essere eseguite sia sull'entità padre che sulle entità figlie. Uno dei principali benefici di questa trasformazione è il risparmio di memoria, poiché non vengono generati valori nulli e non è necessario l'uso di un attributo di tipo, come un campo TIPO per distinguere le sottoclassi. Tuttavia, comporta un numero maggiore di accessi al database, poiché per operare su una delle entità figlie (ad esempio, E1 o E2), è necessario accedere prima all'entità padre (E0). Nonostante l'aumento degli accessi, questa soluzione offre una rappresentazione chiara e precisa delle relazioni tra la superclasse e le sottoclassi, mantenendo i dati organizzati in maniera efficiente. Seconda Soluzione La trasformazione in cui la generalizzazione scompare e rimangono solo le specializzazioni implica che tutte le entità figlie ereditano gli attributi dell’entità padre. Questa modalità di trasformazione, identificata come la trasformazione b), può essere applicata solo in presenza di generalizzazioni totali. In caso contrario, le occorrenze dell'entità padre (E0) che non appartengono né a E1 né a E2 non sarebbero rappresentate, risultando in una perdita di dati. Questo approccio è vantaggioso quando si devono gestire operazioni distinte e specializzate su E1 ed E2, in quanto permette di lavorare separatamente sulle sottoclassi. Inoltre, consente un risparmio di memoria, poiché non si creano valori nulli e non è necessario un attributo di tipo, come il campo TIPO, per distinguere le sottoclassi. Tuttavia, l’applicabilità limitata alle generalizzazioni totali rende questo metodo adatto solo a specifici contesti. Terza Soluzione L'accorpamento delle specializzazioni nella generalizzazione prevede che l'entità padre inglobi le entità figlie, assumendone tutti gli attributi. La trasformazione a) si applica in situazioni in cui le operazioni non distinguono tra le occorrenze e gli attributi delle varie entità figlie, permettendo di trattare le informazioni come un insieme unico. Questo approccio riduce il numero di accessi al database, poiché tutte le occorrenze e gli attributi, precedentemente distribuiti tra l'entità padre e le entità figlie, sono ora concentrati in un'unica entità. Tuttavia, questa soluzione introduce la problematica dei valori nulli, poiché ci saranno occorrenze dell'entità padre che non trovano corrispondenza in nessuna delle entità figlie (generalizzazione parziale), così come occorrenze di una sottoclasse che non hanno riscontro nell'altra sottoclasse. Inoltre, è necessario introdurre un attributo supplementare per distinguere le occorrenze provenienti dalle diverse entità figlie. Questo comporta una maggiore occupazione di memoria, non solo a causa dei campi con valori nulli, ma anche per la presenza dell'attributo aggiuntivo utilizzato per differenziare le entità. Sebbene questa trasformazione possa semplificare alcune operazioni, risulta meno efficiente in termini di utilizzo dello spazio. La soluzione mista Questo approccio, particolarmente adatto per gerarchie parziali e sovrapposte, prevede la sostituzione delle sottoclassi con un'unica sottoclasse in associazione 1:1 con la superclasse. In questo scenario, la sottoclasse unica eredita tutti gli attributi delle sottoclassi originarie e sarà identificata esternamente dalla superclasse. Questo implica che la nuova entità figlia sarà l'unica sottoclasse e conterrà: Tutti gli attributi precedentemente appartenenti alle diverse sottoclassi. Tutte le associazioni a cui partecipavano le sottoclassi originali. In aggiunta, viene introdotto un attributo "Tipo" che serve a distinguere a quale sottoclasse ciascuna occorrenza appartiene. Questo attributo permette di mantenere la distinzione tra le occorrenze provenienti dalle diverse sottoclassi, pur semplificando la struttura gerarchica in un’unica sottoclasse. 14.0 Algebra relazionale Operazioni insiemistiche [Notazione Classica] L'algebra relazionale è un insieme di operazioni che permette di manipolare e interrogare i dati all'interno di un database relazionale. Queste operazioni lavorano su relazioni (ossia tabelle) e producono come risultato altre relazioni. In particolare, l'algebra relazionale si basa fortemente sulle operazioni insiemistiche, dato che le relazioni possono essere considerate come insiemi di tuple (righe di dati). Le principali operazioni insiemistiche che possiamo applicare a due relazioni sono: Unione Intersezione Differenza Queste operazioni richiedono che le due relazioni abbiano lo stesso schema, cioè che abbiano gli stessi attributi con lo stesso tipo e lo stesso ordine. Vediamo nel dettaglio come funzionano ciascuna di queste operazioni. 14.1 Unione di Relazioni Definizione L'unione tra due relazioni 𝒓𝟏 e 𝒓𝟐 è un'operazione che restituisce una nuova relazione contenente tutte le tuple presenti. Notazione 𝑟 = 𝑟1 ∪ 𝑟2 Formalizzazione 𝑟 = {𝑡 ∣ 𝑡 ∈ 𝑟1 ∨ 𝑡 ∈ 𝑟2 } 14.2 Intersezione di Relazioni Definizione L'intersezione tra due relazioni 𝒓𝟏 e 𝒓𝟐 è un'operazione che restituisce una nuova relazione contenente solo le tuple presenti in entrambi. Notazione 𝑟 = 𝑟1 ∩ 𝑟2 Formalizzazione 𝑟 = {𝑡 ∣ 𝑡 ∈ 𝑟1 ∧ 𝑡 ∈ 𝑟2 } 14.3 Differenza tra Relazioni Definizione La differenza tra due relazioni 𝒓𝟏 e 𝒓𝟐 è un'operazione che restituisce una nuova relazione contenente solo le tuple presenti in una ma non in entrambe. Notazione 𝑟 = 𝑟1 − 𝑟2 Formalizzazione 𝑟 = {𝑡 ∣ 𝑡 ∈ 𝑟1 ∧ 𝑡 ∈/𝑟2 } 14.4 Operazioni di modifica dello stato Nel contesto del modello relazionale, le operazioni sulle relazioni (ossia tabelle) sono essenziali per la gestione e manipolazione dei dati. Le operazioni di base considerate includono inserimento, cancellazione e modifica di tuple (righe) all'interno di una relazione. In questa premessa, ci focalizziamo su come queste operazioni siano definite inizialmente per una singola tupla e, successivamente, si estendano a operazioni che coinvolgono più tuple attraverso query SQL. 14.4.1 Modello di Operazioni su una Singola Tupla Inizialmente, il modello relazionale considerava solo operazioni che coinvolgono una singola tupla alla volta all'interno di una relazione. Questo approccio è utile per operazioni semplici e di base sulle tabelle, come: Insert: inserimento di una nuova tupla nella relazione. Delete: rimozione di una tupla esistente dalla relazione. Update: modifica dei valori di una tupla già presente nella relazione. Operazione di Insert L'operazione di insert permette di aggiungere una nuova tupla alla relazione esistente. Obiettivo: Inserire una nuova riga (tupla) in una tabella. Sintassi concettuale: 𝑖𝑛𝑠𝑒𝑟𝑡(𝑟, 𝑡) Esempio: INSERT INTO Studenti (Nome, Cognome, Matricola) VALUES ('Giulia', 'Neri', 1004); Operazione di Delete L'operazione di delete rimuove una tupla specifica dalla relazione, basandosi su una condizione che identifica quale tupla eliminare. Obiettivo: Rimuovere una riga (tupla) esistente dalla tabella. Sintassi concettuale: 𝑑𝑒𝑙𝑒𝑡𝑒(𝑟, 𝑐𝑜𝑛𝑑𝑖𝑧𝑖𝑜𝑛𝑒) Esempio: DELETE FROM Studenti WHERE Matricola = 1004; Operazione di Update L'operazione di update consente di modificare i valori di una tupla specifica nella relazione, basandosi su una condizione. Obiettivo: Modificare i valori di una o più colonne di una riga (tupla) esistente nella tabella. Sintassi concettuale: 𝑢𝑝𝑑𝑎𝑡𝑒(𝑟, 𝑎𝑡𝑡𝑟𝑖𝑏𝑢𝑡𝑜, 𝑣𝑎𝑙𝑜𝑟𝑒_𝑛𝑢𝑜𝑣𝑜, 𝑐𝑜𝑛𝑑𝑖𝑧𝑖𝑜𝑛𝑒) Esempio: UPDATE Studenti SET Cognome = 'Verdi' WHERE Matricola = 1004; 15.0 Operazioni Relazionali Nel modello relazionale, le operazioni classiche che permettono di estrarre e manipolare dati dalle relazioni sono proiezione, selezione e join. Queste operazioni costituiscono la base dell'algebra relazionale, che descrive in maniera formale il modo in cui si manipolano insiemi di tuple (righe) in un database relazionale. 15.1 Proiezione Definizione Procedurale La proiezione è un'operazione che consente di selezionare solo un sottoinsieme di attributi da una relazione, eliminando gli altri. In altre parole, la proiezione crea una nuova relazione che contiene solo alcune colonne della relazione originale. Notazione Se r è una relazione e 𝐴1 , 𝐴2 , … , 𝐴𝑘 sono attributi di r, la proiezione su questi attributi si indica come: 𝜋𝐴1,𝐴2.…,𝐴𝑘 (𝑟) In SQL, l'operazione di proiezione si realizza selezionando specifiche colonne di una tabella con il comando SELECT. SELECT A1, A2,..., Ak FROM r; Esempio Supponiamo di avere una tabella Studenti con i seguenti attributi: Nome, Cognome, Matricola, AnnoIscrizione. Se vogliamo ottenere solo i nomi e i cognomi degli studenti, possiamo eseguire la seguente proiezione: SELECT Nome, Cognome FROM Studenti; 15.2 Selezione Definizione Procedurale La selezione è un'operazione che consente di estrarre tutte le tuple di una relazione che soddisfano una determinata condizione. Si basa su un predicato che filtra le righe in base ai valori dei loro attributi. Notazione Se r è una relazione e condizione è un predicato logico applicabile alle tuple di r, la selezione si indica come: 𝜎𝑐𝑜𝑛𝑑𝑖𝑧𝑖𝑜𝑛𝑒 (𝑟) In SQL, la selezione si realizza utilizzando il comando WHERE per specificare la condizione logica. SELECT * FROM r WHERE condizione; Esempio Supponiamo di avere la tabella Studenti e vogliamo selezionare tutti gli studenti che si sono iscritti nell'anno 2023. In SQL, eseguiremo la seguente query: SELECT * FROM Studenti WHERE AnnoIscrizione = 2023; 15.3 Soddisfacimento di una Condizione di Selezione nel Modello Relazionale Nel modello relazionale, la selezione filtra le tuple di una relazione in base a una condizione. Le condizioni possono essere atomiche o combinate. 1. Condizione Atomica Una condizione atomica è un confronto tra attributi o tra un attributo e una costante: 𝐴𝜃𝐵: confronta due attributi con 𝜃 ∈ {=, ≠, , ≤, ≥}. 𝐴 = 𝑐: confronta un attributo A con una costante c. 2. Condizione di Selezione Una condizione di selezione può essere atomica o composta tramite operatori logici: 𝑣𝑐1 ∧ 𝑣𝑐2 : vera se entrambe le condizioni sono vere (AND). 𝑣𝑐1 ∨ 𝑣𝑐2 : vera se almeno una condizione è vera (OR). ¬𝑣𝑐1 : vera se la condizione è falsa (NOT). 3. Soddisfacimento di una Condizione Una tupla t soddisfa una condizione χ se: Confronto tra attributi: 𝑡[𝐴] 𝜃 𝑡[𝐵]. Confronto con costanti: 𝑡[𝐴] 𝜃 𝑐. Combinazioni complesse si valutano tramite gli operatori logici. Se tutte le condizioni sono soddisfatte, la tupla è selezionata. 15.4 Confronto tra Stringhe in SQL: Utilizzo dell'Operatore LIKE Nell’ambito della gestione dei dati, il confronto tra stringhe è un'operazione fondamentale, specialmente quando si lavora con database relazionali come SQL. Le stringhe, che rappresentano sequenze di caratteri, possono essere confrontate in vari modi, e uno degli operatori più utili in SQL per questo scopo è l'operatore LIKE. Questo operatore consente di eseguire confronti basati su modelli, facilitando la ricerca di stringhe che soddisfano determinati criteri. 15.4.1 Operatore LIKE L'operatore LIKE viene utilizzato per cercare un pattern specifico all'interno di una colonna di tipo stringa. A differenza dei confronti standard che richiedono una corrispondenza esatta, LIKE consente di utilizzare caratteri jolly per rendere la ricerca più flessibile. Caratteri Riservati Due caratteri speciali sono comunemente usati con l'operatore LIKE: Carattere di percentuale (%): Rappresenta un numero arbitrario di caratteri. Può essere usato all'inizio, alla fine o nel mezzo di una stringa per indicare che non ci sono restrizioni su cosa può apparire in quella posizione. Esempio: LIKE 'A%' troverà tutte le stringhe che iniziano con 'A'. Carattere di sottolineatura (_): Rappresenta un singolo carattere. È utile quando si desidera specificare che esattamente un carattere deve apparire in una posizione specifica. Esempio: LIKE '_e_' troverà tutte le stringhe che hanno 'e' come secondo carattere. 15.5 Operatori Aritmetici e di Ordinamento in SQL Operatori Aritmetici Gli operatori aritmetici sono utilizzati per effettuare calcoli matematici su colonne di tipo numerico. I principali operatori aritmetici in SQL sono: Addizione (+): somma due valori. Sottrazione (-): sottrae un valore da un altro. Moltiplicazione (*): moltiplica due valori. Divisione (/): divide un valore per un altro. Esempio di Utilizzo degli Operatori Aritmetici Supponiamo di voler visualizzare il nome, il cognome e lo stipendio incrementato di 10 euro per tutti gli impiegati di un’azienda. La query SQL potrebbe essere la seguente: SELECT Nome, Cognome, Stipendio + 10 AS StipendioIncrementato FROM ANAGRAFICA; Ordinamento dei Risultati La clausola ORDER BY viene utilizzata per ordinare le righe restituite da una query. Si può ordinare in ordine crescente (ASC) o decrescente (DESC). Per impostazione predefinita, l'ordinamento è crescente. Esempio di Ordinamento dei Risultati Per visualizzare i cognomi degli studenti ordinati alfabeticamente e i nomi ordinati in senso decrescente, la query sarà: SELECT Cognome, Nome FROM ANAGRAFICA ORDER BY Cognome ASC, Nome DESC; 15.6 Prodotto Cartesiano Il prodotto cartesiano è un concetto fondamentale in algebra relazionale, utilizzato per combinare tutte le possibili coppie di tuple provenienti da due relazioni diverse. Questo concetto è alla base di molte operazioni in SQL, dove le tabelle (relazioni) possono essere combinate per generare nuovi insiemi di dati. Definizione Data due relazioni 𝑟1 e 𝑟2 definite sugli schemi 𝑅(𝑋1 ) e 𝑅(𝑋2 ) con 𝑋1 ∩ 𝑋2 = ∅ (ovvero, non ci sono attributi in comune tra le due relazioni), il prodotto cartesiano restituisce un'istanza di relazione r definita su 𝑅(𝑋1 ∪ 𝑋2 ). Rappresentazione Formale La relazione r è definita come: 𝑟 = 𝑟1 × 𝑟2 = {𝑡 = ⟨𝑡1 , 𝑡2 ⟩ ∣ 𝑡1 ∈ 𝑟1 ∧ 𝑡2 ∈ 𝑟2 } Dove 𝑡1 è una tupla di 𝑟1 e 𝑡2 è una tupla di 𝑟2. Ogni tupla t nel risultato finale r è la concatenazione di una tupla da 𝑟1 e una da 𝑟2. 15.7 Dot Notation La dot notation (notazione a punti) è un metodo utile per distinguere gli attributi comuni tra due relazioni, specialmente quando si esegue un prodotto cartesiano tra di esse. In un prodotto cartesiano, ogni riga della prima relazione viene combinata con ogni riga della seconda, il che può portare a conflitti di nome se entrambe le relazioni hanno attributi con lo stesso nome. Per evitare ambiguità, utilizziamo la dot notation per fare riferimento a ciascun attributo: r1.id: riferimento all'attributo id della relazione r1 r2.id: riferimento all'attributo id della relazione r2 r1.nome: riferimento all'attributo nome della relazione r1 r2.codice: riferimento all'attributo codice della relazione r2 15.8 Theta Join ed Equi Join La THETA JOIN è un'operazione fondamentale in algebra relazionale che consente di combinare due relazioni in base a una condizione specifica, espressa tramite un operatore di confronto (θ) tra gli attributi delle due relazioni. Questa operazione può includere diversi tipi di confronti, come uguaglianze, maggiore, minore, e così via. Definizione di THETA JOIN Data due relazioni: r1 su 𝑅(𝑋1 ) r2 su 𝑅(𝑋2 ) con 𝑋1 ∩ 𝑋2 = ∅ (ovvero, gli attributi di 𝑟1 e 𝑟2 non si sovrappongono), la THETA JOIN può essere rappresentata come: 𝜒 ≡ 𝑟1 𝜃𝑟2 dove θ è una condizione che può essere un'uguaglianza o un'altra relazione di confronto. EQUI JOIN Se la condizione θ è un'uguaglianza tra i valori di un attributo di 𝒓𝟏 e quelli di 𝒓𝟐 , si parla di EQUI JOIN. In questo caso, la condizione di join è espressa come: 𝑟1. 𝑎𝑡𝑡𝑟 = 𝑟2. 𝑎𝑡𝑡𝑟 In SQL, l'operazione di JOIN è fondamentale per combinare righe di due o più tabelle in base a una condizione comune. Ci sono due modi principali per eseguire un JOIN: in modo implicito e in modo esplicito. Vediamo entrambe le modalità in dettaglio. 1. JOIN Implicito Il JOIN implicito si ottiene applicando la definizione di equi-join come prodotto cartesiano, seguito da una selezione. In questa modalità, le tabelle sono elencate nella clausola FROM e la condizione di join è specificata nella clausola WHERE. SELECT * FROM r1, r2 WHERE Condizione; 2. JOIN Esplicito Il JOIN esplicito, d'altra parte, richiede una sintassi più chiara e espressiva in cui l'operazione di join è definita direttamente nella query. Utilizza la clausola JOIN seguita da ON per specificare la condizione di join. SELECT * FROM r1 JOIN r2 ON Condizione; 15.9 Tuple Dondolanti Il concetto di “tupla dondolante” (o dangling tuple) si riferisce a quelle righe di una relazione che non trovano corrispondenza nell'altra relazione durante un'operazione di JOIN, in particolare quando si utilizza un EQUI JOIN. Queste tuple rimangono "sospese" o "dondolanti" perché non hanno un legame diretto con le tuple dell'altra relazione. Esempio di Tupla Dondolante ID Nome Mansione Codice Descrizione 1 Mario Rossi 101 101 Responsabile 2 Anna Bianchi 102 102 Operativo 3 Luca Verdi 103 104 Amministratore Risultato dell'EQUI JOIN L'output di questa query sarà: ID Nome Mansione Codice Descrizione 1 Mario Rossi 101 101 Responsabile 2 Anna Bianchi 102 102 Operativo In questo caso, la terza tupla della tabella ANAGRAFICA_DIPENDENTI (Luca Verdi) non appare nel risultato dell'operazione di join, perché non esiste una mansione corrispondente (103) nella tabella MANSIONI. Tupla Dondolante Tupla dondolante: La terza tupla in ANAGRAFICA_DIPENDENTI (ID = 3, Nome = Luca Verdi, Mansione = 103) è considerata una tupla dondolante, poiché non ha alcun corrispettivo nella tabella MANSIONI. Questo significa che l'impiegato Luca Verdi non ha una mansione associata. Implicazioni delle Tuple Dondolanti Integrità dei Dati: Le tuple dondolanti possono indicare problemi di integrità nei dati, in particolare se ci si aspetta che ogni dipendente abbia una mansione valida. Filtri e Query: È importante tener conto delle tuple dondolanti quando si scrivono query, poiché potrebbero portare a risultati non attesi. Join Estesi: Se si desidera includere anche le tuple dondolanti nel risultato, si potrebbe utilizzare una LEFT JOIN, che garantisce di includere tutte le righe dalla tabella a sinistra (in questo caso, ANAGRAFICA_DIPENDENTI), anche se non ci sono corrispondenze nella tabella a destra (MANSIONI). 15.10 Natural Join l JOIN NATURALE è una forma particolare di THETA JOIN in cui le condizioni di uguaglianza vengono applicate automaticamente su tutti gli attributi con lo stesso nome nelle due relazioni. Questo tipo di join è utile quando si desidera combinare le righe di due tabelle in base a colonne che condividono nomi e significato, senza dover specificare manualmente le condizioni di uguaglianza per ogni coppia di attributi. Definizione di JOIN NATURALE Data due relazioni r1 e r2, se condividono un attributo comune AcAcAc (ad esempio, un ID, un codice o un nome), il JOIN NATURALE restituisce solo le righe in cui i valori di AcAcAc sono uguali in entrambe le tabelle. Sintassi La sintassi per un JOIN NATURALE è: SELECT * FROM r1 NATURAL JOIN r2; Esempio di JOIN NATURALE Supponiamo di avere due tabelle: ID Nome Mansione Codice Descrizione 1 Mario Rossi 101 101 Responsabile 2 Anna Bianchi 102 102 Operativo 3 Luca Verdi 103 103 Manager Se desideriamo eseguire un JOIN NATURALE sulle colonne Mansione di ANAGRAFICA_DIPENDENTI e Codice di MANSIONI, il risultato del JOIN NATURALE sarà: SELECT * FROM ANAGRAFICA_DIPENDENTI NATURAL JOIN MANSIONI; Risultato del JOIN NATURALE L'output della query sarà: ID Nome Mansione Codice Descrizione 1 Mario Rossi 101 101 Responsabile 2 Anna Bianchi 102 102 Operativo 3 Luca Verdi 103 103 Manager 15.10.1 Join Esterno Il JOIN ESTERNO è un’operazione fondamentale in SQL che consente di includere tutte le righe di una o entrambe le tabelle coinvolte in una query, anche se non esistono corrispondenze nelle altre tabelle. Questo è particolarmente utile per evitare di perdere dati significativi, specialmente quando si lavora con tuple dondolanti. Tipi di JOIN ESTERNO Ci sono tre tipi principali di JOIN ESTERNO: JOIN ESTERNO SINISTRO (LEFT OUTER JOIN) JOIN ESTERNO DESTRO (RIGHT OUTER JOIN) JOIN ESTERNO COMPLETO (FULL OUTER JOIN) 1. JOIN ESTERNO SINISTRO (LEFT OUTER JOIN) Il JOIN ESTERNO SINISTRO restituisce tutte le righe dalla relazione a sinistra (rs) e le righe corrispondenti dalla relazione a destra (rd). Se non ci sono corrispondenze nella relazione a destra, i valori delle colonne provenienti da quest'ultima saranno NULL. SELECT * FROM rs LEFT OUTER JOIN rd ON condizione; Esempio Consideriamo di avere due tabelle: ID Nome ID_Ordine ID_Cliente Prodotto 1 Marco 1 1 Prodotto A 2 Lucia 2 2 Prodotto B 3 Giovanni Eseguendo un LEFT JOIN: SELECT * FROM CLIENTI LEFT JOIN ORDINI ON CLIENTI.ID = ORDINI.ID_Cliente; Risultato ID Nome ID_Ordine ID_Cliente Prodotto 1 Marco 1 1 Prodotto A 2 Lucia 2 2 Prodotto B 3 Giovanni NULL NULL NULL In questo caso, Giovanni appare nel risultato con valori NULL per le colonne della tabella ORDINI, poiché non ha effettuato alcun ordine. 2. JOIN ESTERNO DESTRO (RIGHT OUTER JOIN) Il JOIN ESTERNO DESTRO restituisce tutte le righe dalla relazione a destra (rd) e le righe corrispondenti dalla relazione a sinistra (rs). Se non ci sono corrispondenze nella relazione a sinistra, i valori delle colonne provenienti da quest'ultima saranno NULL. SELECT * FROM rs RIGHT OUTER JOIN rd ON condizione; Esempio Utilizzando le stesse tabelle, eseguendo un RIGHT JOIN: ID Nome ID_Ordine ID_Cliente Prodotto 1 Marco 1 1 Prodotto A 2 Lucia 2 2 Prodotto B NULL NULL NULL NULL NULL 3. JOIN ESTERNO COMPLETO (FULL OUTER JOIN) Il JOIN ESTERNO COMPLETO combina i risultati del LEFT JOIN e del RIGHT JOIN. Restituisce tutte le righe da entrambe le relazioni, con valori NULL nelle colonne dove non ci sono corrispondenze. SELECT * FROM rs FULL OUTER JOIN rd ON condizione; ID Nome ID_Ordine ID_Cliente Prodotto 1 Marco 1 1 Prodotto A 2 Lucia 2 2 Prodotto B 3 Giovanni NULL NULL NULL NULL NULL NULL NULL NULL 15.11 Ridenominazione L'operazione di ridenominazione è un concetto fondamentale in teoria delle basi di dati, particolarmente quando si lavora con relazioni in SQL. Essa permette di cambiare i nomi degli attributi di una relazione, mantenendo intatti i dati e la loro struttura. Definizione di Ridenominazione Data una relazione r definita su uno schema R(X), dove X è un insieme di attributi, possiamo definire un nuovo insieme di attributi Y avente la stessa cardinalità di X. Condizioni Cardinalità: L'insieme Y deve avere la stessa cardinalità di X. Ciò significa che se X ha k attributi, anche Y deve avere k attributi. Dominio: È necessario definire un ordinamento tra gli attributi di X e quelli di Y. Se indichiamo gli attributi di X come 𝐴1 , 𝐴2 , … , 𝐴𝑘 e quelli di Y come 𝐵1 , 𝐵2 , … , 𝐵𝑘 , deve valere la proprietà che 𝑑𝑜𝑚(𝐴𝑖 ) = 𝑑𝑜𝑚(𝐵𝑖 ) per ogni i da 1 a k. Operazione di Ridenominazione L'operazione di ridenominazione si può formalizzare come segue: Si parte dalla relazione r e si applica una trasformazione per rinominare gli attributi. In notazione formale, se r ha attributi 𝐴1 , 𝐴2 , … , 𝐴𝑘 , e desideriamo rinominarli in 𝐵1 , 𝐵2 , … , 𝐵𝑘 , l'operazione può essere espressa come: 𝜌𝑌 (𝑟) = 𝑛𝑢𝑜𝑣𝑎 𝑟𝑒𝑙𝑎𝑧𝑖𝑜𝑛𝑒 𝑐𝑜𝑛 𝑎𝑡𝑡𝑟𝑖𝑏𝑢𝑡𝑖 𝐵1 , 𝐵2 , … , 𝐵𝑘 Esempio di Ridenominazione Supponiamo di avere la seguente relazione EMPLOYEES: ID Nome Mansione 1 Mario Manager 2 Anna Developer 3 Luca Tester Se desideriamo rinominare gli attributi da ID, Nome, Mansione a Employee_ID, Full_Name, Job_Title, possiamo applicare l'operazione di ridenominazione: SELECT ID AS Employee_ID, Nome AS Full_Name, Mansione AS Job_Title FROM EMPLOYEES; Risultato della Ridenominazione La nuova relazione avrà la seguente forma: Employee_ID Full_Name Job_Title 1 Mario Manager 2 Anna Developer 15.12 Alias 3 Luca Tester Gli alias consentono di creare nomi temporanei per tabelle o colonne, facilitando la leggibilità e la scrittura delle query. Concetto di Alias Alias per Tabelle: Permette di fare riferimento a una tabella con un nome diverso, utile in situazioni di auto-join o quando si devono fare confronti all'interno della stessa tabella. Alias per Colonne: Consente di rinominare le colonne nel risultato della query, migliorando la chiarezza dei risultati. Esempio di Utilizzo degli Alias Consideriamo il seguente scenario: vogliamo trovare i nomi e i cognomi dei dipendenti che hanno la stessa mansione di un dipendente specifico (ad esempio, Carlo Rossi), ma escludendo Carlo Rossi stesso dai risultati. Nome Cognome Mansione Carlo Rossi Manager Anna Bianchi Developer Marco Verdi Manager Luca Neri Tester La query che implementa questo concetto utilizzando alias è la seguente: SELECT T1.Nome, T1.Cognome FROM ANAGRAFICA_DIPENDENTI T1, ANAGRAFICA_DIPENDENTI T2 WHERE (T1.Mansione = T2.Mansione) AND (T2.Nome = 'Carlo') AND (T2.Cognome = 'Rossi') AND (T1.Nome 'Carlo') AND (T1.Cognome 'Rossi'); Spiegazione della Query Alias: T1: è un alias per la prima istanza della tabella ANAGRAFICA_DIPENDENTI. Rappresenta i dipendenti che vogliamo elencare. T2: è un alias per la seconda istanza della stessa tabella. Rappresenta il dipendente di riferimento (Carlo Rossi) di cui vogliamo trovare i colleghi. Condizioni: (T1.Mansione = T2.Mansione): confronta la mansione del dipendente di riferimento (T2) con quella di altri dipendenti (T1). (T2.Nome = 'Carlo') AND (T2.Cognome = 'Rossi'): specifica il dipendente di riferimento. (T1.Nome 'Carlo') AND (T1.Cognome 'Rossi') : esclude Carlo Rossi dai risultati. Supponendo che la tabella ANAGRAFICA_DIPENDENTI contenga i dati sopra menzionati, il risultato della query sarebbe: Nome Cognome Marco Verdi 15.13 Aggregazione L'aggregazione si riferisce a un insieme di operazioni che combinano valori da più righe in un'unica riga. Queste operazioni sono fondamentali per ottenere informazioni sintetiche dai dati e sono particolarmente utili in contesti di reportistica e analisi dei dati. Alcune delle funzioni di aggregazione più comuni sono: COUNT(): Questa funzione restituisce il numero totale di righe che soddisfano una certa condizione. È utile per determinare quanti elementi esistono in un dataset, come ad esempio il numero totale di vendite. SUM(): Somma i valori di una colonna numerica, fornendo un totale. Può essere utilizzata, ad esempio, per calcolare il totale delle vendite in un periodo specifico. AVG(): Calcola la media aritmetica dei valori in una colonna. È utile per avere un’idea del valore medio di una variabile, come il prezzo medio di un prodotto. MIN() e MAX(): Queste funzioni restituiscono rispettivamente il valore minimo e massimo di una colonna. Possono essere utilizzate, ad esempio, per trovare il prezzo più basso o più alto tra i prodotti in vendita. 15.14 Raggruppamento Il raggruppamento consente di aggregare i dati in base ai valori di uno o più attributi. Quando utilizziamo la clausola GROUP BY, stiamo dicendo al database di creare dei gruppi di righe che condividono un valore comune in uno o più attributi. Questo è utile quando vogliamo applicare funzioni di aggregazione a ogni gruppo separato. Esempio di Raggruppamento Immagina di avere una tabella che registra le vendite di vari prodotti in diversi periodi. Se desideriamo sapere quante vendite sono state fatte per ciascun prodotto, possiamo utilizzare il raggruppamento. Ad esempio, se stiamo analizzando le vendite per ogni prodotto, raggruppando i dati per il nome del prodotto, possiamo sommare le vendite per ottenere il totale per ciascun prodotto. 15.14.1 Clausole di Aggregazione GROUP BY La clausola GROUP BY è fondamentale per il raggruppamento. Essa permette di specificare gli attributi sui quali vogliamo raggruppare i dati. Tutti gli attributi elencati nella clausola SELECT devono essere presenti anche nella clausola GROUP BY, a meno che non siano utilizzati con una funzione di aggregazione. HAVING La clausola HAVING è utilizzata per filtrare i risultati dopo che il raggruppamento è stato effettuato. Mentre la clausola WHERE viene utilizzata per filtrare righe prima del raggruppamento (sulle singole righe), HAVING consente di applicare condizioni sui risultati aggregati. Questo significa che possiamo, ad esempio, filtrare i gruppi che superano un certo valore totale dopo aver eseguito la somma. Differenze Chiave tra WHERE e HAVING Caratteristica WHERE HAVING Filtra le righe prima del Fase di Esecuzione Filtra i risultati dopo il raggruppamento. raggruppamento. Può essere utilizzato con qualsiasi Utilizzato per applicare condizioni sui risultati Utilizzo colonna di dati. aggregati. Funzioni di Non può utilizzare funzioni di Può utilizzare funzioni di aggregazione. Aggregazione aggregazione. Usato per filtrare righe in base a Usato per filtrare gruppi di dati risultanti da una Scopo condizioni specifiche. query aggregata. SELECT * FROM Tabella WHERE SELECT colonna1, COUNT(*) FROM Tabella GROUP BY Esempio di Utilizzo colonna1 = valore; colonna1 HAVING COUNT(*) > 1; 16.0 Operatori Insiemistici in SQL In SQL, gli operatori insiemistici consentono di combinare i risultati di due o più query trattandoli come se fossero insiemi matematici. Le princi