nare.it  

IMPLEMENTAZIONE

L'utilizzo della base di dati deve avvenire, come detto, principalmente per mezzo di Internet, per cui l'applicazione verrà realizzata con script ASP (Active Server Pages) che utilizzano l'interfaccia ADO (ActiveX Data Object) per l'accesso alla base di dati; il linguaggio di scripting che utilizzeremo sarà JavaScript.
Non è di interesse per gli scopi di questa documentazione entrare nel dettaglio di ogni aspetto dell'implementazione; trattandosi di un lavoro volto a descrivere le modalità di progetto delle basi di dati, ci limiteremo in questa sede a descrivere come le operazioni individuate in fase di Analisi dei Requisiti possano essere realizzate in termini di interrogazioni sulla base di dati o di inserimento/aggiornamento/eliminazione dalla stessa.
Tutte le interazioni con la base di dati seguiranno un pattern simile al seguente:
// Apertura di una connessione al database

// Aggiornamento di dati nel database
          e / oppure
// Recupero di dati dal database
// Utilizzo dei risultati dell'interrogazione

// Rilascio risorsee chiusura connessione
Ci occuperemo, in questa sede, solo di descrivere le istruzioni SQL necessarie ad espletare il compito; solo dove necessario saranno forniti ulteriori dettagli implementativi (uso di transazioni, uso di cursori aggiornabili etc.).

INTERROGAZIONI

Le operazioni richieste dall'applicazione (vedi Analisi dei Requisiti) sono di aggiornamento, alcune, e altre di interrogazione; queste ultime sono le più frequenti e ce ne occupiamo in questo paragrafo.
Per migliorare la leggibilità del codice, e in alcuni casi per rendere possibili interrogazioni altrimenti non realizzabili, è opportuno definire delle viste sulla base di dati. Le viste, in Access, vengono dette query; una query può essere definita utilizzando una interfaccia grafica oppure scrivendo direttamente l'istruzione SQL: adotteremo questo secondo metodo.
Elenchiamo di seguito le query che abbiamo definito sulla nostra base di dati e vedremo, quindi, come le operazioni richieste dall'applicazione possano essere facilmente espresse in base a tali query.

Query _Intervista00_Short

SELECT
I.IDredattore,I.IDartista,I.DataIntervista,I.TitoloIntervista
FROM
INTERVISTA AS I;

Query _Intervista01_Redattore

SELECT
I.*, R.CognomeRedattore,R.NomeRedattore
FROM
_Intervista00_Short AS I LEFT JOIN REDATTORE AS R ON I.IDredattore=R.UserID;
Questa query fornisce tutte le interviste, ciascuna con il relativo redattore. Si utilizza un join esterno per assicurare che vengano restituite tutte le interviste, anche se di fatto è impossibile, visti i vincoli imposti, che a un record della tabella INTERVISTA non sia correlato un record della tabella REDATTORE.

Query _Intervista02_Redattore_Top3

SELECT
TOP 3 I.*,R.CognomeRedattore,R.NomeRedattore
FROM
_Intervista00_Short AS I LEFT JOIN REDATTORE AS R ON I.IDredattore=R.UserID
ORDER BY
I.DataIntervista DESC;
Questa query è analoga alla precedente, però restituisce solo le tre interviste più recenti.

Query _Recensione00_Short

SELECT
R.CodiceDisco,R.IDredattore,R.DataRecensione,R.TitoloRecensione
FROM
RECENSIONE AS R;

Query _Recensione01_Redattore

SELECT
X.*,R.CognomeRedattore,R.NomeRedattore
FROM
_Recensione00_Short AS X LEFT JOIN REDATTORE AS R ON X.IDredattore=R.UserID;
Questa query è analoga a quella che correla le interviste con i redattori.

Query _Recensione02_Redattore_Top3

SELECT
TOP 3 X.*,R.CognomeRedattore,R.NomeRedattore
FROM
_Recensione00_Short AS X LEFT JOIN REDATTORE AS R ON X.IDredattore=R.UserID
ORDER BY
X.DataRecensione DESC;
Questa query è come la precedente, ma restituisce solo le tre interviste più recenti.

Query _Artista00_Short

SELECT
IDartista,NomeArtista,NazioneArtista
FROM
ARTISTA;

Query _Disco00_Short

SELECT
D.Codice,D.TitoloDisco,D.CasaDiscografica,D.AnnoDisco,G.IDgenere,G.NomeGenere
FROM
DISCO AS D LEFT JOIN GENERE AS G ON D.IDgenere=G.IDgenere;

Query _Disco01_Recensione

SELECT
D.*,R.IDredattore,R.CognomeRedattore,R.NomeRedattore,R.TitoloRecensione,R.DataRecensione
FROM
_Disco00_Short AS D LEFT JOIN _Recensione01_Redattore AS R ON D.Codice=R.CodiceDisco;
Questa query restituisce i dischi ciascuno con i dati relativi alla recensione del disco (compresi i dati del redattore); si usa il join esterno per assicurarsi che vengano recuperati tutti i dischi, anche quelli che non hanno recensione.

Query _Disco02_Artisti

SELECT
D.*,A.IDartista,A.NomeArtista,A.NazioneArtista
FROM
_Disco00_Short AS D LEFT JOIN
(REALIZZAZIONE AS R LEFT JOIN _Artista00_Short AS A ON A.IDartista=R.IDartista)
ON D.Codice=R.CodiceDisco;
Questa query restituisce i dischi e, per ognuno di essi, i dati degli artisti che lo hanno realizzato; per ogni disco, quindi, si avranno in generale più record nel risultato, uno per ogni artista che ha realizzato il disco.

Query _Disco03_Recensione_Artisti

SELECT
DR.*,DA.IDartista,DA.NomeArtista,DA.NazioneArtista
FROM
_Disco01_Recensione AS DR LEFT JOIN _Disco02_Artisti AS DA ON DR.Codice=DA.Codice;
Questa query non fa altro che combinare le due precedenti, restituendo così i dischi, ciascuno con la relativa recensione (se esiste) e i dati degli artisti che lo hanno realizzato.
Questa query, o anche la precedente, può essere utilizzata per realizzare l'operazione OP06.

Query _Disco04_Short_Top5

SELECT
TOP 5 Codice,TitoloDisco,AnnoDisco,CasaDiscografica,
VotoMedio,NumeroVoti,Valutazione,D.IDgenere,NomeGenere
FROM
DISCO AS D LEFT JOIN GENERE AS G ON D.IDgenere=G.IDgenere
ORDER BY
Valutazione DESC,VotoMedio DESC,NumeroVoti DESC,AnnoDisco DESC,Codice;
Questa query restituisce i 5 dischi con il valore dell'attributo Valutazione più alto.

Query _Disco05_Recensione

SELECT
D.*,G.NomeGenere,R.IDredattore,R.CognomeRedattore,R.NomeRedattore,
R.TitoloRecensione,R.DataRecensione
FROM
(DISCO AS D LEFT JOIN GENERE AS G ON D.IDgenere=G.IDgenere)
LEFT JOIN _Recensione01_Redattore AS R ON D.Codice=R.CodiceDisco;
Questa query è analoga a _Disco02_Recensione, con l'unica differenza che, per ogni disco, fornisce anche l'indicazione del genere del disco.

Query _Disco06_Recensione_Artisti

SELECT
DR.*,DA.IDartista,DA.NomeArtista,DA.NazioneArtista
FROM
_Disco05_Recensione AS DR LEFT JOIN _Disco02_Artisti AS DA ON DR.Codice=DA.Codice;
Questa query è analoga a _Disco03_Recensione_Artisti, ma fornisce anche il genere di ogni disco.

Query __Recensione__Recenti

SELECT
R.*,D.TitoloDisco,D.AnnoDisco,D.IDgenere,D.NomeGenere,D.CasaDiscografica,
D.IDartista, D.NomeArtista, D.NazioneArtista
FROM
_Recensione02_Redattore_Top3 AS R LEFT JOIN _Disco02_Artisti AS D ON R.CodiceDisco=D.Codice
ORDER BY
R.DataRecensione DESC,D.AnnoDisco DESC,R.CodiceDisco,D.NomeArtista;
Questa query restituisce le recensioni più recenti, con i dati del disco recensito; per ogni disco vengono forniti anche i dati relativi agli artisti che lo hanno realizzato. Riguardo quest'ultimo aspetto, osserviamo che la query _Recensione02_Redattore_Top3 riveste una particolare importanza in quanto permette si selezionare "preventivamente" le recensioni più recenti, cosa che non si sarebbe potuta fare "direttamente" in questa interrogazione utilizzando la specifica TOP 3 in quanto per ogni disco (e quindi per ogni recensione) vi sono in generale più record, uno per ogni artista che ha realizzato il disco.
Questa query realizza l'operazione OP01.

Query __Intervista__Recenti

SELECT
I.*,A.NomeArtista,A.NazioneArtista
FROM
_Intervista02_Redattore_Top3 AS I LEFT JOIN _Artista00_Short AS A ON I.IDartista=A.IDartista
ORDER BY
I.DataIntervista DESC,A.NomeArtista,I.CognomeRedattore;
Questa query restituisce le tre interviste più recenti, con i dati dell'artista intervistato.
Questa query realizza l'operazione OP02.

Query __Disco__Preferiti

SELECT
D.*,A.IDartista,A.NomeArtista,A.NazioneArtista
FROM
_Disco04_Short_Top5 AS D LEFT JOIN
(REALIZZAZIONE AS R LEFT JOIN _Artista00_Short AS A ON A.IDartista=R.IDartista)
ON D.Codice=R.CodiceDisco
ORDER BY
D.Valutazione DESC,D.VotoMedio DESC,D.NumeroVoti DESC,D.AnnoDisco DESC,D.Codice,A.NomeArtista;
Questa query restituisce i dati dei 5 dischi che presentano il valore più alto di Valutazione, unitamente ai dati degli artisti che lo hanno realizzato. Ancora, la query _Disco04_Short_Top5 riveste grande importanza perché ci permette di selezionare in anticipo i 5 dischi "preferiti".
Questa query realizza l'operazione OP03.

Query __Disco__Genere

SELECT
D.*
FROM
_Disco02_Artisti AS D
ORDER BY
D.NomeGenere,D.TitoloDisco,D.Codice,D.NomeArtista;
Questa query è banale: restituisce i dischi e i relativi artisti. La utilizziamo solo per ragioni di leggibilità.
Questa query può essere utilizzata per realizzare l'operazione OP07.

Query __Disco__All

SELECT
DR.*,DA.IDartista,DA.NomeArtista,DA.NazioneArtista,B.NumeroBrano,B.TitoloBrano
FROM
(_Disco05_Recensione AS DR LEFT JOIN _Disco02_Artisti AS DA ON DR.Codice=DA.Codice)
LEFT JOIN BRANO AS B ON DR.Codice=B.CodiceDisco
ORDER BY
DR.TitoloDisco,DR.Codice,DA.NomeArtista,B.NumeroBrano;
Questa query restituisce tutti i dati relativi a ciascun disco, compresi i dati della recensione (se esiste, altrimenti i campi corrispondenti assumeranno valore NULL), i dati degli artisti che hanno realizzato il disco, l'elenco dei brani del disco. L'utilizzo dei join esterni è fondamentale per assicurare che vengano riportati tutti i dischi, indipendentemente dal fatto che abbiano o meno la recensione o dei brani.
Questa query può essere utilizzata per realizzare l'operazione OP09.

Query __Artista__Genere

SELECT
A.*,G.IDgenere,G.NomeGenere
FROM
_Artista00_Short AS A INNER JOIN
(CARATTERIZZAZIONE AS C LEFT JOIN GENERE AS G ON C.IDgenere=G.IDgenere)
ON A.IDartista=C.IDartista;
Questa query restituisce gli artisti e, per ciascuno di essi, restituisce i generi che lo caratterizzano; gli attributi relativi al genere saranno ovviamente nulli per gli artisti che non hanno realizzato alcun disco e quindi non sono caratterizzati da alcun genere. La query è banale e la utilizziamo per mere ragioni di leggibilità.
Questa query può essere utilizzata per realizzare l'operazione OP05.

Query __Artista__All

SELECT
A.*,S.Url,I.DataIntervista,I.TitoloIntervista,I.IDredattore AS IDredInt,
I.CognomeRedattore AS CognRedInt,I.NomeRedattore AS NomRedInt,
D.Codice,D.TitoloDisco,D.CasaDiscografica,D.AnnoDisco,D.IDgenere,D.NomeGenere,
D.DataRecensione,D.TitoloRecensione,D.IDredattore AS IDredRec,
D.CognomeRedattore AS CognRedRec,D.NomeRedattore AS NomRedRec,
D.IDartista AS artID,D.NomeArtista AS artNome,D.NazioneArtista AS artNazione
FROM
((ARTISTA AS A LEFT JOIN _Intervista01_Redattore AS I ON A.IDartista=I.IDartista)
LEFT JOIN
(REALIZZAZIONE AS R LEFT JOIN _Disco03_Recensione_Artisti AS D ON R.CodiceDisco=D.Codice)
ON A.IDartista=R.IDartista) LEFT JOIN SITO AS S ON A.IDartista=S.IDartista
ORDER BY
A.NomeArtista,S.Url,D.TitoloDisco,D.Codice,D.NomeArtista,I.DataIntervista,I.IDredattore;
Questa query è, forse, la più complessa; essa restituisce tutti i dati relativi a ciascun artista, compresi i siti, le interviste rilasciate dall'artista, i dischi realizzati e, per ciascuno di questi, l'elenco degli artisti che lo hanno realizzato. Osserivamo l'utilizzo dei join esterni, necessari per assicurare che vengano forniti anche i dati relativi agli artisti che non hanno siti, o che non hanno rilasciato interviste, o che non hanno realizzato dischi.
Questa query può essere utilizzata per realizzare l'operazione OP08.

Osserviamo che l'operazione OP04 può essere realizzata interrogando direttamente la tabella ARTISTA, senza alcuna necessità di utilizzare una query.
Osserviamo anche che le operazioni OP08 e OP09 si sarebbe potute realizzare diversamente. La soluzione adottata, in entrambi i casi, permette di realizzare l'intera operazione mediante una sola interrogazione e quindi accedendo una sola volta alla base di dati; per contro, il risultato è costituito da una grosssa quantità di informazioni con moltissima ridondanza: ad esempio, nel caso di un disco realizzato da 8 artisti e che ha 10 brani, il risultato sarà una tabella di 80 record e, in ognuno di essi, le informazioni relative al disco (titolo, anno, casa discografica, recensione etc.) si ripetono tal quali; ciò aumenta i tempi di trasferimento dei dati dalla base di dati all'applicazione. Inoltre, i dati stessi non sono opportunamente strutturati e quindi è necessario un certo sforzo di programmazione per organizzarli opportunamente, il che richiede tempo di elaborazione. Per contro, le stesse operazioni si possono realizzare mediante più interrogazioni distinte (ad esempio, per un disco si possono prima prelevare i dati propri del disco, poi quelli degli artisti che lo realizzano, poi quelli relativi ai brani inclusi); questa seconda soluzione è di certo più leggibile, non richiede sforzo di programmazione per organizzare i dati e riduce la quantità di informazioni che devono essere trasferite dal database all'applicazione; lo svantaggio consiste nel fatto che occorre accedere in più riprese alla base di dati e ogni accesso ha bisogno del suo tempo affinché venga stabilita la connessione al database. Un esame attento dei tempi di esecuzione, in presenza di diverse situazioni di carico sulla base di dati (accessi simultanei da parte di più utenti) e di diverse istanze della base di dati, esula dagli scopi di questa trattazione ma, nella realtà, andrebbe effetutata con cura.

AGGIORNAMENTI

Le operazioni di aggiornamento saranno organizzate in transazioni, secondo il seguente pattern (dove conn è un oggetto ADO Connection):
conn.Open(); //Apertura connesisone al database
try {
  conn.BeginTrans(); //Inizio transazione
  // ... operazioni di aggiornamento
  conn.CommitTrans(); //Transazione conclusa con successo
  }
catch(err) {
  //Si è verificato un errore nel blocco try
  conn.RollBackTrans(); //Transazione annullata
  }
finally {
  conn.Close();
  };
Operando in questo modo, qualunque errore si verifichi nel blocco try, eventualmente anche un tentativo di aggiornamento alla base di dati che ne violi le regole di integrità, viene "catturato" facendo sì che la transazione venga annullata, il che comporta il ripristino delle condizioni precedenti all'inizio della transazione, assicurando così la consistenza della base di dati. Il meccanismo delle transazioni deve essere usato in quanto, per molte delle operazioni richieste, è necessario effettuare modifiche dei dati in tabelle diverse e tra loro correlate ed è quindi necessario impedire che solo una parte degli aggiornamenti necessari venga eseguita, la qual cosa porterebbe inevitabilmente ad una inconsistenza nei dati. In particolare, le transazioni sono utilizzate per assicurare il rispetto dei vincoli di integrità interrelazionali discussi in Progettazione Fisica che non abbiamo potuto esprimere nella definizione della base dei dati, a causa delle limitazioni di Access (che, in particolare, non permette l'utilizzo di comandi SQL come check o create assertion).
Non ci soffermeremo sulle singole operazioni di aggiornamento ma piuttosto descriveremo in dettaglio la sequenza dei passi da seguire per portare a termine l'operazione; descrizioni più accurate saranno fornite dove necessario.
Prima di continuare, facciamo una osservazione sui cursori utilizzabili in ADO; per quanto ci interessa, considereremo essenzialmente due tipi di cursori: statico, che consente la sola lettura dei dati, e dinamico, che consente anche l'aggiornamento. L'utilizzo di quest'ultimo è necessario in alcuni casi. Consideriamo il semplice esempio dell'inserimento di un nuovo disco, immaginando per semplicità che esso sia realizzato da un solo artista già presente nella base di dati e caratterizzato dal valore x per l'attributo IDartista. Per inserire il nuovo disco, dovremo inserire i valori forniti nella tabella DISCO e poi dovremo anche inserire un record in REALIZZAZIONE per indicare che il disco è realizzato dall'artista x; il problema sorge perché il valore da assegnare al campo IDartista nel nuovo record di REALIZZAZIONE è noto, mentre non lo è il valore da assegnare al campo CodiceDisco, in quanto il campo Codice in DISCO è definito come Contatore, e quindi è il DBMS che assegna automaticamente il valore ad esso. Per poter ricavare con sicurezza il valore che è stato assegnato a Codice per il nuovo record inserito in DISCO, al fine di poterlo utilizzare per inserire correttamente il nuovo record in REALIZZAZIONE, dovremo usare un cursore dinamico. Un tale cursore ammette un metodo AddNew che consente di inserire un nuovo record nella struttura sulla quale il cursore è definito (nel nostro caso, la tabella DISCO); dopo l'esecuzione del metodo AddNew, il cursore si posiziona proprio sul record appena inserito per cui basta prelevare il valore corrispondente del campo di interesse (Codice, nel nostro esempio). Ecco la sintassi utilizzata a tale scopo (dove conn è un oggetto ADO Connection):
var ElencoNomiCampi = new Array(...);
var ElencoValori = new Array(...);
var r = Server.CreateObject("ADODB.Recordset");
r.CursorType = 0;
r.LockType = 3; //Cursore dinamico utilizzabile per aggiornamenti
r.Open("SELECT * FROM DISCO",conn);
r.AddNew(ElencoNomiCampi,ElencoValori);
// Il cursore è ora posizionato sul record appena inserito
var CodiceNuovoDisco = String(r.Fields.Item("Codice").value);
r.Close();
// Il valore di CodiceNuovoDisco può ora essere usato
// per operare correttamente sulle altre tabelle da aggiornare
Nel seguito, ipotizzeremo di aver definito una funzione:

     dynInsert(conn,NomeTabella,ElencoNomiCampi,ElencoValori,NomeCampoRitorno)

che realizzi le operazioni appena descritte, restituendo il valore che, per il nuovo record, è stato assegnato al campo il cui nome è stato fornito in NomeCampoRitorno; relativamente all'esempio di sopra, si sarebbe potuta chiamare la funzione come:

     var CodiceNuovoDisco=dynInsert(conn,"DISCO",ElencoNomiCampi,ElencoValori,"Codice")

Vediamo come ogni operazione possa essere implementata.

OPERAZIONE OP10
Modifica voto medio, numero voti e valutazione disco

L'utente immette un voto (numero fra 0 e 10) per uno specifico disco; i valori dei campi del record vengono modificati secondo la formula vista in precedenza (vedi Progettazione ...); sia NuovoVoto la variabile numerica contenente il valore scelto dall'utente e CodiceDiscoCorrente il codice del disco votato.
UPDATE DISCO SET
  NumeroVoti=NumeroVoti+1,
  VotoMedio=(NumeroVoti*VotoMedio+NuovoVoto)/(NumeroVoti+1),
  Valutazione=(NumeroVoti*VotoMedio+NuovoVoto)
WHERE Codice=CodiceDiscoCorrente
Occorre, però, fare i conti con la precisione macchina; infatti è possibile che, a causa delle inevitabili approssimazioni nei calcoli matematici, alcuni valori violino il vincolo di integrità Valutazione=VotoMedio*NumeroVoti, ancorché i dati siano in effetti validi; per ovviare al problema "alleggeriamo" il vincolo, imponendo non l'uguaglianza stretta ma l'uguaglianza approssimata, ammettendo errori dell'1%; ciò può essere fatto formulando il vincolo come segue:
(Valutazione*0,99 < VotoMedio*NumeroVoti) AND (VotoMedio*NumeroVoti < Valutazione*1,01)

OPERAZIONE OP12
Inserimento recensione

DATI FORNITI DALL'UTENTE
  • Titolo Recensione
  • Testo Recensione
  • Voto Recensione
  • Disco Recensito (Codice)
DATI RICAVATI DALL'APPLICAZIONE
  • Data Recensione
  • Redattore (UserID)
PASSI TRANSAZIONE
  1. Inserimento nuovo record in tabella RECENSIONE
IMPLEMENTAZIONE
var values=...;
/*
  Costruzione dell'array di valori forniti dall'utente e ricavati dall'applicazione;
  la data/ora della recensione è posta pari alla data/ora corrente;
  Il valore per IDredattore si può ricavare in quanto il redattore ha effettuato il login
*/

var SQL=
"INSERT INTO RECENSIONE "+
   "(TitoloRecensione,TestoRecensione,DataRecensione,"+
   "VotoRecensione,CodiceDisco,IDredattore) "+
   "VALUES ("+values+")";
conn.Execute(SQL);

OPERAZIONE OP13
Inserimento intervista

DATI FORNITI DALL'UTENTE
  • Titolo Intervista
  • Testo Intervista
  • Artista Intervistato (IDartista)
DATI RICAVATI DALL'APPLICAZIONE
  • Data Intervista
  • Redattore (UserID)
PASSI TRANSAZIONE
  1. Inserimento nuovo record in tabella INTERVISTA
IMPLEMENTAZIONE
var values=...;
/*
  Costruzione dell'array di valori forniti dall'utente e ricavati dall'applicazione;
  la data/ora della intervista è posta pari alla data/ora corrente;
  Il valore per IDredattore si può ricavare in quanto il redattore ha effettuato il login
*/

var SQL=
"INSERT INTO INTEVISTA "+
   "(TitoloIntervitsa,TestoIntervista,DataIntervista,"+
   "IDartista,IDredattore) "+
   "VALUES ("+values+")";
conn.Execute(SQL);

OPERAZIONE OP14
Modifica recensione

DATI FORNITI DALL'UTENTE
  • Titolo Recensione
  • Testo Recensione
  • Voto Recensione
DATI RICAVATI DALL'APPLICAZIONE
  • Disco Recensito (Codice)
PASSI TRANSAZIONE
  1. Aggiornamento dei campi (TitoloRecensione,TestoRecensione,VotoRecensione) del record della tabella RECENSIONE avente valore del campo CodiceDisco pari al valore dato
IMPLEMENTAZIONE
var SQL=
"UPDATE RECENSIONE SET "+
   "TitoloRecensione='"+titoloRec+"',"+
   "TestoRecensione='"+testoRec+"',"
   "VotoRecensione="+votoRec+" "
   "WHERE CodiceDisco="+codiceDisco;
conn.Execute(SQL);

OPERAZIONE OP15
Modifica intervista

DATI FORNITI DALL'UTENTE
  • Titolo Intervista
  • Testo Intervista
DATI RICAVATI DALL'APPLICAZIONE
  • Artista Intervistato (IDartista)
  • Redattore (UserID)
  • Data Intervista (DataIntervista)
PASSI TRANSAZIONE
  1. Aggiornamento dei campi (TitoloIntervista,TestoIntervista) del record della tabella INTERVISTA avente valori dei campi (UserID,IDartista,DataIntervista) pari ai valori dati
IMPLEMENTAZIONE
var SQL=
"UPDATE INTERVISTA SET "+
   "TitoloIntervista='"+titoloInt+"',"+
   "TestoIntervista='"+testoInt+"',"
   "WHERE IDartista="+IDartista+
   "  AND IDredattore="+userid+
   "  AND DataIntervista=#"+dataInt+"#";
conn.Execute(SQL);

OPERAZIONE OP16
Inserimento artista

DATI FORNITI DALL'UTENTE
  • Nome Artista
  • Descrizione
  • Nazione
  • Percorso File Foto
DATI RICAVATI DALL'APPLICAZIONE
  • Nessuno
PASSI TRANSAZIONE
  1. Inserimento nuovo record in tabella ARTISTA
IMPLEMENTAZIONE
var values=...;
/*
  Costruzione dell'array di valori forniti dall'utente
*/

var SQL=
"INSERTI INTO ARTISTA"+
   "(NomeArtista,DescrizioneArtista,NazioneArtista,FotoArtista) "+
   "VALUES ("+values+")";
conn.Execute(SQL);

OPERAZIONE OP17
Inserimento disco

DATI FORNITI DALL'UTENTE
  • Titolo Disco
  • Anno Disco
  • Casa Discografica
  • Foto
  • Genere (IDgenere) oppure Nuovo Nome Genere
  • Elenco brani (NumeroBrano,TitoloBrano)
  • Elenco artisti (elenco IDartista)
DATI RICAVATI DALL'APPLICAZIONE
  • Nessuno
PASSI TRANSAZIONE
  1. Se Nuovo Nome Genere:
    1. Apri cursore dinamico su GENERE
    2. Inserisci in GENERE nuovo record
    3. Preleva il valore assegnato, per il nuovo record, a IDgenere
    4. Chiudi cursore su GENERE
  2. Apri cursore dinamico su DISCO
  3. Inserimento record in tabella DISCO, campi (TitoloDisco,AnnoDisco,CasaDiscografica,FotoDisco,IDgenere)
    NB: IDgenere è il valore assegnato direttamente dall'utente oppure, se l'utente ha chiesto di inserire un nuovo genere, è il valore ricavato al passo 1.c
  4. Preleva il valore del campo Codice del disco appena immesso
  5. Chiudi cursore su DISCO
  6. Inserisci nuovi record in BRANO con i valori di (NumeroBrano,TitoloBrano) forniti dall'utente e il valore CodiceDisco pari al valore Codice ottenuto al punto 4
  7. Per ogni IDartista specificato:
    1. inserimento della coppia (CodiceDisco,IDartista) nella tabella REALIZZAZIONE, dove CodiceDisco è pari al valore di Codice ottenuto al punto 4
    2. se non esiste, in CARATTERIZZAZIONE, un record (IDartista,IDgenere) allora inserisci nuovo record (IDartista,IDgenere) nella tabella CARATTERIZZAZIONE

IMPLEMENTAZIONE
/* Definizione delle variabili contenenti i valori da inserire */
var titoloDisco=..., annoDisco=..., casaDisc=..., fotoDisco=...;

/* Booleana, vera se occorre inserire un nuovo genere */
var insertNuovoGenere=...;

/* Hanno senso l'una o l'altra, a seconda che si debba inserire o meno un nuovo genere: */
var idGenereDato=..., nomeNuovoGenere=...;

/* Array contenenti l'elenco ordinato dei titoli dei brani e i valori del campo IDartista per gli artisti che hanno realizzato il disco */
var brani=..., artisti=...;

if (insertNuovoGenere)
  var idGenere=dynInsert(conn,"GENERE",("NomeGenere"),(nomeNuovoGenere),"IDgenere")
else
  var idGenere=idGenereDato;

var nomiCampi=new Array("TitoloDisco","AnnoDisco",CasaDiscografica",FotoDisco","IDgenere");
var elencoValori=new Array(titoloDisco,annoDisco,casaDisc,fotoDisco,idGenere);
var codiceDisco=dynInsert(conn,"DISCO",nomiCampi,elencoValori,"Codice");

var SQL="", i=0;

for (i=0; i<brani.length; i++) {
  SQL="INSERT INTO BRANO (NumeroBrano,TitoloBrano,CodiceDisco) "+
    "VALUES ("+String(i+1)+",'"+brani[i].replace(/'/g,"''")+"',"+ codiceDisco+")";
  conn.Execute(SQL);
  };

for (i=0; i<artisti.length; i++) {
  SQL="INSERT INTO REALIZZAZIONE (CodiceDisco,IDartista) "+
    "VALUES ("+codiceDisco+","+artisti[i]+")";
  conn.Execute(SQL);
  var r=Server.CreateObject("ADODB.Recordset");
  SQL="SELECT * "+
    "FROM CARATTERIZZAZIONE "+
    "WHERE IDartista="+artisti[i]+" AND IDgenere="+idGenere;
  r=conn.Execute(SQL);
  var caratterizzato=!(r.EOF && r.BOF);
  delete r;
  if (!caratterizzato) {
    SQL="INSERT INTO CARATTERIZZAZIONE (IDartista,IDgenere) "+
    "VALUES ("+artisti[i]+","+idGenere+")";
    conn.Execute(SQL);
    }; //end if
  }; //end for
NOTA: Piuttosto che effettuare una interrogazione per verificare se un artista risulta già caratterizzato in base a un genere, si può piuttosto inserire il record (IDartista,IDgenere) in CARATTERIZZAZIONE utilizzando un blocco try...catch: se esiste già un record uguale (cioè, l'artista è già caratterizzato in base a quel genere) il tentativo di inserimento verrà rifiutato e provocherà un errore il quale, però, è "catturato" e non provoca l'annullamento della transazione; se, invece, non esiste un tale record, il record verrà correttamente inserito come richiesto. Pertanto, il ciclo for (i=0; i<artisti.length; i++) può essere riscritto come segue:
for (i=0; i<artisti.length; i++) {
  SQL="INSERT INTO REALIZZAZIONE (CodiceDisco,IDartista) "+
    "VALUES ("+codiceDisco+","+artisti[i]+")";
  conn.Execute(SQL);
  try {
    SQL="INSERT INTO CARATTERIZZAZIONE (IDartista,IDgenere) "+
    "VALUES ("+artisti[i]+","+idGenere+")";
    conn.Execute(SQL); }
  catch(insertRejected) {
    /*Nessuna azione*/ };
  }; //end for

OPERAZIONE OP18
Modifica artista

DATI FORNITI DALL'UTENTE
  • Nome Artista
  • Descrizione Artista
  • Nazione Artista
  • Percorso File Foto
  • Elenco URL da eliminare
  • Elenco URL da aggiungere
DATI RICAVATI DALL'APPLICAZIONE
  • IDartista
PASSI TRANSAZIONE
  1. Aggiornamento record tabella ARTISTA avente valore del campo IDartista pari al valore dato
  2. Eliminazione da SITO dei record aventi campi Url e IDartista pari ai valori dati
  3. Inserimento in SITO delle coppie di valori (IDartista,Url) fornite
IMPLEMENTAZIONE
var SQL=
"UPDATE ARTISTA SET "+
   "NomeArtista='"+nomeArtista+"',"+
   "DescrizioneArtista='"+descrArtista+"',"
   "NazioneArtista="+nazioneArtista+" "
   "FotoArtista="+fotoArtista+" "
   "WHERE IDartista="+idArtista;
conn.Execute(SQL);

for (var i=0; i<urlDaEliminare.length; i++) {
   SQL=
     "DELETE FROM SITO "+
     "WHERE IDartista="+idARtista+"AND "+
     "Url='"+urlDaEliminare[i]+"'";
   conn.Execute(SQL);
};

for (i=0; i<urlDaAggiungere.length; i++) {
   SQL=
     "INSERT INTO SITO (IDartista,Url) "+
     "VALUES ("+idArtista+",'"+
     urlDaAggiungere[i]+"')";
   conn.Execute(SQL);
};

OPERAZIONE OP19
Modifica disco

DATI FORNITI DALL'UTENTE
  • Titolo Disco
  • Anno Disco
  • Casa Discografica
  • Percorso File Foto
  • Genere (IDgenere)
  • Eventualmente, Elenco Brani (NumeroBrano,TitoloBrano)
  • Eventualmente, artisti che devono essere tolti dalla realizzazione (IDartista)
  • Eventualmente, artisti che devono essere aggiunti alla realizzazione (IDartista)
DATI RICAVATI DALL'APPLICAZIONE
  • Codice del disco
PASSI TRANSAZIONE
  1. Aggiornamento campo della tabella DISCO avente valore del campo Codice pari al valore dato; consideriamo i valori IDgenereVecchio e IDgenereNuovo; tali valori sono uguali se non si è modificato il genere del disco (NB: tali valori sono entrambi dati)
  2. Se vi è un nuovo elenco brani, cancella da BRANO tutti i record aventi CodiceDisco pari al valore dato, quindi inserisci in BRANO i nuovi record con i valori (NumeroBrano,TitoloBrano) forniti dall'utente e il valore CodiceDisco dato
  3. Per ogni artista (se presente) da togliere dalla realizzazione del disco:
    1. Eliminazione da REALIZZAZIONE del record avente CodiceDisco e IDartista pari ai valori dati
    2. Verifica, attraverso le tabelle REALIZZAZIONE-DISCO, se esistono dischi del genere IDgenereVecchio realizzati dall'artista; se non esistono, elimina il record (IDartista,IDgenereVecchio) da CARATTERIZZAZIONE
  4. Per ogni artista (se presente) da aggiungere alla realizzazione del disco:
    1. Inserimento in REALIZZAZIONE della coppia (CodiceDisco,IDartista) data
    2. Se non esiste già un record (IDartista,IDgenereNuovo) in CARATTERIZZAZIONE, aggiungilo


IMPLEMENTAZIONE
/* Definizione delle variabili */
var titoloDisco=..., annoDisco=..., casaDisc=..., fotoDisco=...;
var codice=...,idGenereVecchio=...,idGenereNuovo=...;

/* Array contenenti il nuovo elenco ordinato dei titoli dei brani del disco, l'elenco degli artisti da aggiungere alla realizzazione del disco e l'elenco degli artisti da togliere dalla realizzazione del disco*/
var brani=...,artistiAdd=...,artistiDel=...;

var SQL="", i=0;

SQL="UPDATE DISCO SET "+
"TitoloDisco='"+titoloDisco+"',"+
"AnnoDisco="+annoDisco+","+
"CasaDiscografica='"+casaDisc+"',"+
"FotoDisco='"+fotoDisco+"',"+
"IDgenere="idGenereNuovo+
" WHERE Codice="+codice;
conn.Execute(SQL);

if (brani.length>0) {
  SQL="DELETE FROM BRANI WHERE CodiceDisco="+codice;
  conn.Execute(SQL);
  for (i=0; i<brani.length; i++) {
    SQL="INSERT INTO BRANO (NumeroBrano,TitoloBrano,CodiceDisco) "+
      "VALUES ("+String(i+1)+",'"+brani[i].replace(/'/g,"''")+"',"+ codice+")";
    conn.Execute(SQL);
    };
  };

for (i=0; i<artistiDel.length; i++) {
  SQL="DELETE FORM REALIZZAZIONE WHERE "+
      "IDartista="+artistiDel[i]+" AND "+
      "CodiceDisco="+codice;
  conn.Execute(SQL);
  SQL="DELETE FROM CARATTERIZZAZIONE WHERE "+
      "IDartista="+artistiDel[i]+
      " AND IDgenere="+idGenereVecchio+
      " AND NOT EXISTS ("+
          "SELECT * FROM "+
            "(DISCO AS D LEFT JOIN GENERE AS G ON D.IDgenere=G.IDgenere) "+
            "RIGHT JOIN REALIZZAZIONE AS R ON R.CodiceDisco=D.Codice "+
          "WHERE "+
            "(R.IDartista="+artistiDel[i]+" AND"+
            " G.IDgenere="+idGenereVecchio+")"+
          ")";
  conn.Execute(SQL);
};

for (i=0; i<artistiAdd.length; i++) {
  SQL="INSERT INTO REALIZZAZIONE (CodiceDisco,IDartista) "+
    "VALUES ("+codiceDisco+","+artistiAdd[i]+")";
  conn.Execute(SQL);
  var r=Server.CreateObject("ADODB.Recordset");
  SQL="SELECT * "+
      "FROM CARATTERIZZAZIONE "+
      "WHERE IDartista="+artistiAdd[i]+
      " AND IDgenere="+idGenereNuovo;
  r=conn.Execute(SQL);
  var caratterizzato=!(r.EOF && r.BOF);
  delete r;
  if (!caratterizzato) {
    SQL="INSERT INTO CARATTERIZZAZIONE (IDartista,IDgenere) "+
    "VALUES ("+artistiAdd[i]+","+idGenereNuovo+")";
    conn.Execute(SQL);
    }; //end if
  }; //end for

OPERAZIONE OP20
Eliminazione recensioni e interviste

DATI FORNITI DALL'UTENTE
  • Nessuno
DATI RICAVATI DALL'APPLICAZIONE
  • Data odierna
PASSI TRANSAZIONE
  1. Elimina dalle tabelle RECENSIONE e INTERVISTA tutti i record per i quali il valore del campo DataRecensione (ovvero DataIntervista) rappresenta una data più vecchia di 6 mesi rispetto alla data odierna
IMPLEMENTAZIONE

Utilizziamo la funzione DateDiff, proprietaria di Access, che permette di calcolare intervalli di tempo intercorrenti tra due date; l'implementazione è molto semplice.
var SQL=
"DELETE FROM RECENSIONE "+
   "WHERE DateDiff('m',DataRecensione,Now())>6";
conn.Execute(SQL);

var SQL=
"DELETE FROM INTERVISTA "+
   "WHERE DateDiff('m',DataIntervista,Now())>6";
conn.Execute(SQL);