Articolo pubblicato sul n. 40 di MCmicrocomputer (Edizioni Technimedia Srl - Roma) nell'aprile 1985

MCmicrocomputer


Data Base Management Systems:
Costruiamo un data base

di Andrea de Prisco

A conclusione del nostro ciclo di articoli sulle basi di dati, questo mese e il prossimo, parleremo di una piccola applicazione creata per il Commodore 64: il Galileo/J, interamente scritto in Basic. Su questo numero vi verrà consegnato tutto il listato con una rapida spiegazione dei comandi per i più impazieti: il mese prossimo, con più calma, vedremo in pratica, qualche semplice esempio d'uso.Copertina del numero di MCmicrocomputer contenente l'articolo

   Il Galileo/J

   Il Galileo/J deve il suo nome al Galileo, un sistema di gestione per basi di dati tuttora in fase di realizzazione presso il Dipartimento di Scienze dell'Informazione dell'Univesità degli Studi di Pisa, al quale è ispirato nei suoi lineamenti generali.

   Si tratta di un data base seguente il modello semantico dei dati, o per essere più precisi un subset dello stesso. Infatti, il modello semantico dei dati, discusso nella terza puntata di questa serie, è il più avanzato dei modelli visti anche perché è il più nuovo, ancora oggetto di studio in tutto il mondo. Dire di averlo implementato completamente su un personal computer sarebbe una grossa eresia. Il Galileo/J è usato in questo contesto per mostrare qualcosa di soltanto prossimo a sistemi di gestione per basi di dati più seri.

   Gioca tutte le sue carte sul meccanismo della classificazione, mediante il quale entità diverse vengono considerate omogenee, da inserire cioè in una stessa classe. Il concetto di entità è, del resto, assai intuitivo: sono le cose che ci interessano, appartenenti al mondo osservato.

   Per fare qualche esempio, il caro amico Accalappiagalli Gregorio, il libro "Software, violenza e vivisezioe", una fattura di vendita sono entità. Se abbiamo a che fare con questo genere di oggetti, potremo descrivere la nostra base di dati creando tre classi: la prima denominata Persone, la seconda Libri, la terza Fatture. Posto che le fatture riguardino un negozio di libri, e conseguentemente, una serie di libri acquistati e la persona acquirente, potremo mettere le tre classi in associazione, permettendo facili operazioni di navigazione tra i dati, per risalire da un libro del nostro negozio, all'insieme di persone che l'hanno acquistato, o roba simile.

   Di questo però ci siamo già occupati sul n. 35 di MC, nell'articolo riguardante appunto il modello semantico al quale vi rimandiamo per eventuali chiarimenti.

   Il Galileo/J dispone di alcuni operatori per definire le classi di una base, nonché per recuperare, editare o cancellare elementi dalla stessa. Il meccanismo di Aggregazione serve per definire la struttura di una classe, non senza stabilire le associazioni con altri dati. Ricordiamo che nel modello semantico le associazioni con altri elementi sono proprietà degli elementi stessi. Ritornando all'esempio precedente, una fattura avrà come proprietà un numero progressivo, una data, un Cliente (associazione con la classe Persone) e l'elenco dei libri acquistati (associazione multipla con la classe Libri).

   Nel nostro caso, definendo la struttura di una classe, oltre alle proprietà chiave e costanti, elencheremo l'insieme delle proprietà associazione, realizzate col meccanismo delle chiavi esterne proprie questa volta dei modelli relazionali.

   Una chiave esterna è chiave per un'altra classe: identifica univocamente un altro elemento. Nessun vincolo è posto sull'esistenza dello stesso, in quanto eventuali associazioni pendenti rimangono tali. Se diciamo cioè che una fattura riguarda il cliente Arcibaldo Polizzi, il fatto che questo non sia presente nella classe Persone non vieta di inserire lo stesso la fattura in classe. E' quando avremo completato l'inserimento di tutte le persone (compreso cioè il Polizzi) che avremo la correlazione desiderata.

   Chiariremo in seguito con vari esempi. Per quanto riguarda gli operatori per la ricerca, possiamo chiedere al sistema di trovare tutti quelli, o soltanto di mostrarci il primo, che soddisfano una particolare condizione. E' possibile poi muoverci in una classe chiedendo il successivo elemento che soddisfa la condizione data.

   Ogni associazione con altri elementi (di altre classi o della stessa) viene esplicitata al primo livello: se un elemento della classe A è in associazione con un elemento della classe B, e gli elementi della classe B sono in associazione con elementi della classe C, accade che accedendo alla classe A, otterremo anche gli elementi correlati in classe B (e non quelli in classe C), accedendo di contro in classe B, otterremo anche gli elementi di C. Ciò per evitare cicli infiniti: infatti, se le classi sono 2, A e B, e ogni elemento di A è in relazione con un elemento di B e viceversa, il sistema comincerebbe a saltare da una classe all'altra, all'infinito, mostrando sempre gli stessi 2 elementi. Cerchiamo di essere un po' più chiari: abbiamo due classi, Maschi e Femmine, con la doppia associazione SposatoCon. E' chiaro che se Ermenegildo è sposato con Ermengarda, deve essere vero anche il viceversa. Se l'esplicitazione delle associazioni, cioè il mostrare oltre all'elemento cercato anche quelli a lui correlati, fosse ricorsiva accadrebbe che chiedendo notizie su Renzo sapremo che questo è sposato con Lucia, la quale è sposata con Renzo, il quale è sposato con Lucia, la quale è sposata con Renzo....until crash (fino a quando non si spacca tutto).

   Definiamo una base

   Per definire una base di dati in Galileo/J, dopo aver stabilito quali classi e quali associazioni tra dati utilizzare, occorre dichiarare le varie classi e per ognuna di esse, la struttura degli elementi. Solo dopo si potrà procedere all'inserimento delle registrazioni che ci interessano.

   Per dichiarare una classe si usa l'operatore class. Senza dilungarci in parole, cominciamo a fare un piccolo esempio: immaginiamo di dover memorizzare alcuni nominativi accompagnati da relativo recapito telefonico. Scriveremo così:

   class Rubrica <-> (Nome-string and Telefono-int)

   Rubrica è il nome della classe, racchiusa tra parentesi il tipo dell'ennupla utilizzata, indicando per ogni campo il tipo. In questo caso abbiamo usato un campo di tipo stringa (il nome) e (and) un campo di tipo intero (il telefono). L'operatore <-> serve solo come delimitatore tra nome della classe e ennupla di definizione. Oltre a questo dovremo scegliere un campo chiave (in questo caso la scelta è solo tra nome e telefono) tramite il quale sarà assicurato l'accesso univoco alla registrazione. Ciò implica (e il sistema avviserà con un messaggio l'eventuale violazione) che non si potranno inserire due registrazioni con uguale campo chiave. Nel nostro caso o sceglieremo di registrare nominativi tutti diversi o con diverso numero telefonico.

   La chiave si specifica subito dopo la descrizione di ennupla, nell'esempio precedente, scegliendo come chiave il nominativo, la dichiarazionne completa sarà:

   class Rubrica <-> (Nome-string and Telefono-int)key(Nome)

   Abbiamo detto che tra parentesi si elencano i vari campi dell'ennupla, specificando per ognuno il tipo. Vediamo quali sono i tipi utilizzabili. Non ci soffermeremo sui tipi string e int, che come facile intuire hanno un comportamento assai familiare. I campi string possono contenere stringhe di caratteri, i campi int, solo valori numerici. Chiaramente il tipo stringa, come in basic, non permette di inserire stringhe molto lunghe, proprio per questo è disponibile anche il tipo page che offre ben 1000 posizioni (un'intera schermata) per annotare più roba. Un altro tipo di dato molto importante è l'extkey disponibile anche al plurale (extkeys). Tramite questo è possibile correlare dati di classi diverse o della stessa classe. Al singolare costruisce una associazione univoca, al plurale un'associazione multipla. Per rendere meglio il concetto, andiamo avanti con i nostri esempi: questa volta vogliamo costruire un indirizzario ottimizzato, specialmente per quanto riguarda le informazioni riguardo le città abitate dalle persone che memorizzeremo.

   Come già più volte descritto, spesso capita che la ridondanza di alcune informazioni appesantisca notevolmente (e inutilmente) tutta la base di dati: occorre sempre modellare bene la struttura delle varie classi. Se ad esempio conosciamo trenta persone a Benevento, è inutile memorizzare trenta volte le informazioni riguardo la città (Nome, Cap, Prefisso). Meglio è memorizzare le città interessate in una classe, e gli indirizzi comprensivi di numero telefonico in un'altra. Un'opportuna associazione, come vedremo, ricostruirà l'indirizzo completo ad ogni interrogazione della base. La prima delle due classi sarà definita così:

  class Città <-> (Località-string and Sigla-string and CAP-string and Prefisso-string)key(Sigla)

   Sigla identifica univocamente la località: quando inseriremo gli elementi in questa classe, indicheremo per ogni città la sigla automobilistica se questa è capoluogo di provincia, due o tre caratteri del nome altrimenti. L'importante è usare una codifica univoca.

   La classe delle persone sarà definità così:

   class Amici <-> (NomeCognome-string and Recapito-string and Residenza-extkey in Città and Telefono-int and Varie-page)key(NomeCognome)

   Amici è il nome della classe, Residenza il puntatore (chiave esterna) nella classe Città, Varie una paginata supplementare (facoltativa) per introdurre note sull'individuo da noi "schedato".

   Inseriamo una città: il comando è make, seguito dal nome della classe da incrementare. Quindi:

   make Città

   alle domande Località, Sigla, Cap e Prefisso, risponderemo secondo le nostre intensioni: inseriremo la città del nostro primo indirizzo. Con:

   make Amici

   potremo introdurre il resto.

Vi rimandiamo all'apposito paragrafo per chiarirvi maggiormente le idee; in questa sede, staremo ancora lontani dai particolari per non confondervi (subito) troppo.

   Vediamo ora l'uso del tipo extkeys per costruire associazioni multiple. In questo caso associeremo ad ogni elemento di una classe, più elementi di un'altra classe, o della stessa. Immaginiamo di aver inserito nominativi e città delle persone che conosciamo. Per curare le nostre public relations costruiamo la classe Appuntamenti, nella quale registreremo per ognuno di essi una data, un luogo (la città) e le persone che dovremo incontrare. Inutile dire che sfrutteremo al massimo l'informazione già presente: ci limiteremo a definire la nostra nuova classe così:

   class Appuntamenti <-> (Data-string and Luogo-extkey in Città and Persone-extkeys in Amici)key(Data)

   per semplicità assumiamo di non avere mai più di un appuntamento al giorno. Quando dovremo inserire un appuntamento, il sistema ci chiederà solo la data, la sigla del luogo e una sequenza di nominativi. Al momento della ricerca, ossia quando saremo interessati a un determinato appuntamento, dando al sistema la data relativa, otterremo una marea di informazioni: luogo, prefisso e CAP della sede d'appuntamento; nome, cognome, indirizzo, residenza e telefono di tutte le persone partecipanti.

   Il Galileo/J e il 64.

   Dopo aver descritto il Galileo/J in generale, vediamo come questo sia stato implementato sul Commodore 64. Per intenderci meglio, caricate il programma e mandatelo in esecuzione: potremo così darvi passo-passo istruzioni per costruire la nostra base di dati. Preferibilmente lasciate sul dischetto solo il programma Galileo/J, in quanto lo spazio-disco richiesto per ogni applicazione non è indifferente.

   Per prima cosa occorre familiarizzare con l'interfaccia utente: i meccanismi del programma che permettono l'interzione uomo-macchina. Dato che il Galileo/J è scritto interamente in Basic, tale interfaccia è (ovviamente) realizzata con delle istruzioni di INPUT per i comandi da impartire, e da istruzioni di PRINT per gli output su video e stampante.

   Subito dopo il Run, appare in alto a sinistra una E seguita dai due punti e dal cursore lampeggiante. Il sistema è pronto per Eseguire un comando: corrisponde più o meno al Ready del basic.

   Anche se non vediamo punti interrogativi, occorre ricordare che siamo sempre in ambiente di INPUT (nel senso Basic che conosciamo) e ciò comporta alcune limitazioni, imposte dal 64: non è possibile digitare input per più di due linee di schermo e non è disponibile un editor sofisticato. Per il primo problema nessuna preoccupazione: se il nostro comando supera le due linee, possiamo battere [RETURN] per disporre di due nuove linee e, eventulmente, anche ripetere il trucchetto quante volte si vuole. Chiaramente occorre non spezzare in due una parola, ma agire sul [RETURN] solo in sostituzione di uno spazio. A proposito di questi, sono obbligatori tra una parola e l'altra tranne il caso in cui sia presente un altro carattere separatore :")","-","(","=". L'editor di schermo è limitato alla linea che stiamo introducendo. Come terminatore di linea si usa il carattere ";" (punto e virgola): serve per far capire al sistema che il comando è stato introdotto e può essere eseguito. Sembra difficile, ma non lo è: basta non farsi prendere dal panico.

   Proviamo a definire l'associazione Indirizzi-città già ampiamente discussa in quest'articolo e nei precedenti. Dobbiamo dichiarare le due classi: tenendo sott'occhio la foto 1 digitate la prima linea del comando cioè "class Amici <-> (". Potremmo continuare a digitare sulla stessa linea, ma per questioni di eleganza battiamo [RETURN] e continuiamo sulla riga successiva definendo la struttura dei vari campi, come mostrato nella foto 1, ricordandovi di battere [RETURN] per passare a nuova linea.

   La specifica len(120) serve per dimensionare lo spazio occupato dal file Relativo che utilizzeremo: 120 indica la lunghezza massima di ogni registrazione. Se nulla è specificato si assume la massima possibile: 254 caratteri per registrazione. Ricordatevi, comunque, che è sempre meglio abbondare.

   Al termine il punto e virgola terminerà il comando: se tutto è andato per il verso giusto sentirete il driver ruotare; se qualcosa non và sarà segnalato un fallimento dell'operazione, con relativa causa.

   Quando il sistema è pronto per un nuovo comando appare nuovamente la E seguita dai due punti. Introduciamo (fedelmente) la definizione della seconda classe, come mostrato nella foto 2, seguendo gli stessi accorgimenti di prima. Al termine il Driver inizializzerà questo nuovo file relativo. Da questo momento in poi, ad ogni comando "make Amici" potremo inserire nome e indirizzo di un nostro conoscente; digitando "make Città" possiamo inserire le città dei nostri conoscenti. Nelle foto 3, 4 e 5, è mostrato l'inserimento di un elemento in ciascuna classe, e un esempio di comando di ricerca.

   Prima di continuare con la descrizione dei comandi disponibili, fermiamoci un attimo per alcune considerazioni. Il tipo page, come detto in precedenza, permette di disporre di una intera paginata per introdurre informazioni circa la registrazione che stiamo memorizzando. Nel caso nostro, se alla domanda "Varie ?" rispondiamo y vedremo ripulire il video: questa è la nostra paginata libera. Possiamo scrivere quel che vogliamo: al termine basterà digitare la sequenza [RETURN] <Chicciolina> [RETURN]. <Chiocciolina>, per intenderci, è il carattere del tasto tra la P e l'asterisco della tastiera del 64.

   Come vedremo anche in seguito, il tipo page ha alcune limitazioni. Ma continuamo ora con le considerazioni di carattere generale.

   Altro punto da tenere sempre ben presente, è il fatto che tutte le parole chiave del Galileo/J sono scritte in minuscolo, mentre per quanto riguarda nomi di campi e classi, sono disponibili sia le minuscole che le maiuscole. Non è possibile usare lo spazio all'interno di un nome (di una classe o di un campo), ma bisogna saldare eventuali doppie denominazioni: nella nostra prima classe definita, abbiamo scritto NomeCognome e non Nome Cognome. Chiaramente ciò non vale per il contenuto delle registrazioni: come è visibile nella 3' foto, abbiamo scritto Vincenzo Tuccillo e non VincenzoTuccillo (tutto attaccato).

   I comandi del Galileo/J 

   Il primo comando visto è class e serve per definire la struttura di una classe dati; la sua sintassi è:

   class NomeClasse <-> (Struttura)

   key(ChiavePrimaria) [len(LunghezzaElementi)];

   tra parentesi quadre la specifica len, come visto opzionale. Il secondo comando costruisce elementi in una classe; la sua sintassi è semplicissima:

   make NomeClasse;

   costruisce un elemento nella classe specificata, chiedendo i valori dei vari campi.

   find NomeClasse with Attributo=Costante;

   trova il primo elemento che soddisfa la condizione specificata dopo il with. Attributo è il nome di un campo degli elementi della classe, Costante è un suo possibile valore. Ad esempi potremmo scrivere find Città with Prefisso=081 o find Amici with Città=NA o roba simile.

   Per trovare eventuali altri elementi che soddisfano la medesima condizione, basta usare il comando:

   next;

   se nessun altro elemento in memoria è trovato o se il next è dato prima di un find, un messaggio di fallimento avviserà l'operatore.

   Per trovare in un sol colpo tutti gli elementi che soddisfano una condizione (ossia senza prima trovare il primo e poi chiedere gli altri a forza di next) è disponibile il comando all con identica sintassi del find. Scriveremo cioè qualcosa del tipo:

   all NomeClasse with Attributo=Costante;

   Sia il find che l'all hanno un particolare formato per scorrere tutti gli elementi di una classe: basta non digitare affatto la condizione, scrivendo solo find NomeClasse; o all NomeClasse;.

   Per dirottare gli output su stampante si usa il comando:

   printer;

   e resta attivo fino a quando non si digita il comando complementare:

   display;

   Le registrazioni trovate saranno stampate su carta, eccezion fatta per le informazioni di tipo page (ecco la prima limitazione).

   Per conoscere la definizione delle varie classi (se dimentichiamo qualche nome o qualche attributo) è disponibile il comando:

   def;

   che mostra la prima classe. Come per il find, per conoscere le altre, si usa il comando next più volte. Il comando:

   quit;

   serve per terminare una sessione di lavoro: è obbligatorio digitarlo tutte le volte che si smette di usare il Galileo/J. Salva su disco lo stato interno del sistema, in modo da ritrovarsi nello stesso punto la prossima volta. Se non si termina sempre con quit (siete avvisati, è una minaccia) la base di dati può facilmente andare in uno stato inconsistente e occorre ricominciare tutto daccapo.

   Gli ultimi due comandi servono per togliere o modificare un elemento di una classe. In questi soli due casi, è obbligatorio usare la chiave primaria per identificare l'oggetto. Scriveremo cosi:

   destroy NomeClasse with Chiave=costante;

   se vogliamo togliere un elemento o

   edit NomeClasse with Chiave=costante;

  se vogliamo editare una registrazione. I vari campi sono mostrati nuovamente su video e sarà sufficiente battere [RETURN] per conservare l'informazione o digitare la sostituzione opportuna. Unica eccezione per il campo page (seconda limitazione) che dovrà interamente essere reintrodotto.

   Per ora ci fermiamo qui: sul prossimo numero faremo qualche esempio un po' più significativo (specialmente per la compensione). Arrivederci.


Impaginato originale...


Clicca per ingrandire... Clicca per ingrandire...
Clicca per ingrandire... Clicca per ingrandire...
Clicca per ingrandire... Clicca per ingrandire...

Articolo pubblicato su www.digiTANTO.it - per ulteriori informazioni clicca qui