Avanti Indietro Indice

Caso di studio: schema fisico

Concludiamo la progettazione del nostro caso di studio con la progettazione fisica. Non assumeremo l'uso di alcun DBMS specifico ma faremo riferimento ad SQL standard. In particolare questo ci impedirà di definire in SQL le strutture di memorizzazione per le tabelle e gli indici. Lo schema fisico della base di dati TeatroSQL, in tutte le sue componenti (domini, tabelle, asserzioni, trigger e viste) è il seguente:

create schema TeatroSQL 
{  

  create domain tipoBiglietto as varchar(8)
  default "Intero"
  check (tipoBiglietto in ('Intero', 'Ridotto', 'Studenti'))

  create domain mansione as varchar(5)
  check (mansione in ('CA', 'POD', 'CUSRP', 'ACF'))

  create domain tipoProduttore as varchar(7)
  check (tipoProduttore in ('interno', 'esterno'))
  
  create table teatro
  (
    nome             varchar(20) primary key, 
    telefono         varchar(15),
    fax              varchar(15),
    indirizzo        varchar(40) not null,  
    email            varchar(30),
    url              varchar(30)
  ) 
  
  create table biglietteria
  (
    nome             varchar(20) primary key, 
    indirizzo        varchar(40) not null,
    email            varchar(30),
    telefono         varchar(15),  
    teatro           varchar(20) foreign key references teatro(nome)
                                 on update cascade on delete set null
  ) 
  
  create table orario
  (
    biglietteria     varchar(20), 
    giorno           varchar(10), 
    inizio           time, 
    fine             time, 
    primary key(biglietteria, giorno, inizio),
    foreign key biglietteria references biglietteria(nome)
                on update cascade on delete cascade,
  ) 
  
  create table notizia
  (
    data        date,
    ora         time,
    oggetto     varchar(100),
    testo       CLOB,
    primary key(data, ora, oggetto)
  )
  
  create table newsletter
  (
    teatro      varchar(20),
    data        date,
    ora         time,
    oggetto     varchar(100),
    primary key(teatro, data, ora, oggetto),
    foreign key teatro references teatro(nome)
                on update cascade on delete cascade,
    foreign key (data, ora, oggetto) references notizia(data, ora, oggetto)
                on update cascade on delete cascade
  )
  
  create table dipendente
  (
    cf                     char(16) primary key, 
    nome                   varchar(20) not null, 
    cognome                varchar(20) not null,
    dataDiNascita          date,
    luogoDiNascita         varchar(20), 
    residenza              varchar(30), 
    telefonoFisso          varchar(15), 
    telefonoMobile         varchar(15),  
    email                  varchar(30)
  )
  
  create table lavoro
  (
    teatro              varchar(20), 
    dipendente          char(16),
    dataAssunzione      date,
    ruolo               mansione,
    cda                 boolean,
    primary key(teatro, dipendente),
    foreign key teatro references teatro(nome)
                on update cascade on delete cascade,
    foreign key dipendente references dipendente(cf)
                on update cascade on delete cascade,
    constraint RA1 check (
    cda = false or 
    (year(current_date) - year(dataAssunzione) > 10) or
    (year(current_date) - year(dataAssunzione) = 10 and 
       month(current_date) > month(dataAssunzione)) or
    (year(current_date) - year(dataAssunzione) = 10 and 
       month(current_date) = month(dataAssunzione) and
       day(current_date) >= day(dataAssunzione)) 
    )     
  ) 
  
  create table stipendio
  (
    dipendente    char(16), 
    inizio        date, 
    importo       decimal(6,2),
    primary key(dipendente, inizio),
    foreign key dipendente references dipendente(cf)
                on update cascade on delete cascade
  ) 
  
  create table spazio
  (
    nome             varchar(20) primary key, 
    indirizzo        varchar(40) not null,
    pianta           varchar(20),
    capienza         smallint
  ) 
  
  create table luogo
  (
    teatro     varchar(20), 
    spazio     varchar(20),
    primary key(teatro, spazio),
    foreign key teatro references teatro(nome)
                on update cascade on delete cascade
    foreign key spazio references spazio(nome)
                on update cascade on delete cascade
  ) 
  
  create table stagione
  (
    nome           varchar(20), 
    biennio        char(9), 
    teatro         varchar(20),
    primary key(nome, biennio),
    foreign key teatro references teatro(nome)
                on update cascade on delete set null
  )
  
  create table spettacolo
  (
    titolo             varchar(40) primary key,  
    descrizione        CLOB,
    annoProduzione     char(4),
  ) 
  
  create table messaInScena
  (
    data               date,  
    ora                time,
    spazio             varchar(20),
    spettacolo         varchar(40),
    postiDisponibili   smallint,
    prezzoIntero       decimal(5,2),
    prezzoRidotto      decimal(5,2),
    prezzoStudenti     decimal(5,2),
    primary key (data, ora, spazio),
    foreign key spazio references spazio(nome)
                on update cascade on delete set null,
    foreign key spettacolo references spettacolo(titolo)
                on update cascade on delete set null,
    constraint RA3-1 check (postiDisponibili >= 0)                           
  ) 
  
  create table materiale
  (
    file                   varchar(30) primary key,
    tipo                   varchar(10),
    dimensione             integer,
    dataSpettacolo         date,  
    oraSpettacolo          time,
    spazioSpettacolo       varchar(20),
    foreign key (dataSpettacolo, oraSpettacolo, spazioSpettacolo) references
                 messaInScena(data, ora, spazio)
                 on update cascade on delete set null
  )
    
  create table prenotazione
  (
    dataSpettacolo     date,  
    oraSpettacolo      time,
    spazioSpettacolo   varchar(20),
    numero             smallint,
    data               date,
    ora                time,
    posto              varchar(5),
    tipo               tipoBiglietto,
    prezzo             decimal(5,2),
    primary key(spettacolo, numero),
    foreign key (dataSpettacolo, oraSpettacolo, spazioSpettacolo) references
                 messaInScena(data, ora, spazio)
                 on update cascade on delete cascade
  ) 
  
  create table commento
  (
    autore             varchar(20),
    data               date,
    ora                time,
    testo              CLOB,
    dataSpettacolo     date,  
    oraSpettacolo      time,
    spazioSpettacolo   varchar(20),
    primary key (autore, data, ora),
    foreign key (dataSpettacolo, oraSpettacolo, spazioSpettacolo) references
                 messaInScena(data, ora, spazio)
                 on update cascade on delete set null
  )
  
  create table risposta
  (
    autoreRisposta     varchar(20),
    dataRisposta       date,
    oraRisposta        time,
    autore             varchar(20),
    data               date,
    ora                time,
    primary key (autoreRisposta, dataRisposta, oraRisposta), 
    foreign key (autoreRisposta, dataRisposta, oraRisposta) references 
                commento(autore, data, ora)
                on update cascade on delete cascade,
    foreign key (autore, data, ora) references 
                commento(autore, data, ora)
                on update cascade on delete cascade
  )
  
  create table produzione
  (
    produttore     varchar(20), 
    spettacolo     varchar(40),
    primary key(produttore, spettacolo),
    foreign key produttore references produttore(nome)
                on update cascade on delete cascade
    foreign key spettacolo references spettacolo(titolo)
                on update cascade on delete cascade  
  )
  
  create table produttore
  (
    nome           varchar(20) primary key, 
    tipo           tipoProduttore, 
    indirizzo      varchar(40), 
    telefono       varchar(15), 
    email          varchar(30),
  )
    
  create table interprete
  (
    nome                 varchar(20), 
    ruolo                varchar(15),
    cv                   CLOB,
    primary key(nome)
  )
  
  create table cast
  (
    spettacolo               varchar(40), 
    interprete               varchar(20), 
    primary key(spettacolo, interprete),
    foreign key spettacolo references spettacolo(titolo)
                on update cascade on delete cascade
    foreign key interprete references interprete(nome)
                on update cascade on delete cascade  
  ) 
        
  crate assertion RA2 check (
    not exists (
      select stagione.teatro, stagione.nome, stagione.biennio, count(*)
      from (stagione join proposta on 
           (stagione.nome = proposta.nomeStagione) and 
           (stagione.biennio = proposta.biennioStagione)) 
           join produzione on 
           (proposta.spettacolo = produzione.spettacolo) and
           (stagione.teatro = produzione.produttore)
      group by stagione.teatro, stagione.nome, stagione.biennio
      having count(*) > 2
    )
  )          
    
  crate trigger RA3-2
  after insert on prenotazione
  for each row
  update messaInScena
  set postiDisponibili = postiDisponibili - 1
  where (data = New.dataSpettacolo and 
         ora = New.oraSpettacolo 
         spazio = New.spazioSpettacolo)
    
  
  crate trigger RA3-3
  after delete on prenotazione
  for each row
  update messaInScena
  set postiDisponibili = postiDisponibili + 1
  where (data = Old.dataSpettacolo and 
         ora = Old.oraSpettacolo 
         spazio = Old.spazioSpettacolo)

  
  crate trigger RA3-4
  after insert on messaInScena
  for each row
  update messaInScena
  set postiDisponibili = (select capienza
                          from spazio
                          where nome = New.spazio) 
  where (data = New.data and 
         ora = New.ora 
         spazio = New.spazio)


  crate trigger RA4-1
  after insert on messaInScena
  for each row
  update messaInScena
  set New.prezzoRidotto = New.prezzoIntero * 0.8
  where (data = New.data and 
         ora = New.ora 
         spazio = New.spazio)
  
  
  crate trigger RA4-2
  after insert on messaInScena
  for each row
  update messaInScena
  set New.prezzoStudenti = New.prezzoIntero * 0.5
  where (data = New.data and 
         ora = New.ora 
         spazio = New.spazio)
  

  crate trigger RA4-3
  after update of prezzoIntero on messaInScena
  for each row
  update messaInScena
  set New.prezzoRidotto = New.prezzoIntero * 0.8
  where (data = New.data and 
         ora = New.ora 
         spazio = New.spazio)
  
  
  crate trigger RA4-4
  after update of prezzoIntero on messaInScena
  for each row
  update messaInScena
  set New.prezzoStudenti = New.prezzoIntero * 0.5
  where (data = New.data and 
         ora = New.ora 
         spazio = New.spazio)
  

  crate trigger RA5-1
  after insert on prenotazione
  for each row
  when (tipo = "Intero")
  update prenotazione
  set New.prezzo = (select prezzoIntero
                    from messaInScena
                    where (data = New.dataSpettacolo and 
                           ora = New.oraSpettacolo and
                           spazio = New.spazioSpettacolo))
  where (dataSpettacolo = New.dataSpettacolo and 
         oraSpettacolo = New.oraSpettacolo and
         spazioSpettacolo = New.spazioSpettacolo and 
         numero = New.numero)  
         

  crate trigger RA5-2
  after insert on prenotazione
  for each row
  when (tipo = "Ridotto")
  update prenotazione
  set New.prezzo = (select prezzoRidotto
                    from messaInScena
                    where (data = New.dataSpettacolo and 
                           ora = New.oraSpettacolo and
                           spazio = New.spazioSpettacolo))
  where (dataSpettacolo = New.dataSpettacolo and 
         oraSpettacolo = New.oraSpettacolo and
         spazioSpettacolo = New.spazioSpettacolo and 
         numero = New.numero)  
  

  crate trigger RA5-1
  after insert on prenotazione
  for each row
  when (tipo = "Studenti")
  update prenotazione
  set New.prezzo = (select prezzoStudenti
                    from messaInScena
                    where (data = New.dataSpettacolo and 
                           ora = New.oraSpettacolo and
                           spazio = New.spazioSpettacolo))
  where (dataSpettacolo = New.dataSpettacolo and 
         oraSpettacolo = New.oraSpettacolo and
         spazioSpettacolo = New.spazioSpettacolo and 
         numero = New.numero)  
  
  
  create view spettacoli(teatro, nomeStagione, biennioStagione, 
    titolo, descrizione, data, ora, spazio, indirizzo, postiDisponibili 
    prezzoIntero, prezzoRidotto, prezzoStudenti) as
  select St.teatro, St.nomeStagione, St.biennioStagione, Spe.titolo, 
    Spe.descrizione, Sce.data, Sce.ora, Sce.spazio, Spa.indirizzo, 
    Sce.postiDisponibili, Sce.prezzoIntero, Sce.prezzoRidotto, Sce.prezzoStudenti
  from stagione St, proposta P, spettacolo Spe, messaInScena Sce, spazio Spa
  where (St.nome = P.nomeStagione) and
        (St.biennio = P.biennioStagione) and
        (P.spettacolo = Spe.titolo) and
        (Spe.titolo = Sce.spettacolo)
        (Sce.spazio = Spa.nome)
  order by St.teatro, St.nomeStagione, St.biennioStagione, Sce.data        
  
  
  create view attori(teatro, nomeStagione, biennioStagione, nome, ruolo, cv) as
  select St.teatro, St.nomeStagione, St.biennioStagione, I.nome, I.ruolo, I.cv
  from stagione St, proposta P, spettacolo Sp, cast C, interprete I
  where (St.nome = P.nomeStagione) and
        (St.biennio = P.biennioStagione) and
        (P.spettacolo = Sp.titolo) and
        (Sp.titolo = C.spettacolo) and
        (C.interprete = I.nome) 
  order by St.teatro, St.nomeStagione, St.biennioStagione, I.nome, I.ruolo


  create view dipendenti(teatro, cognome, nome, cf, età, telefonoFisso,
    telefonoMobile, email, stipendio, dataDiAssunzione, ruolo, cda) as
  select L.teatro, D.cognome, D.nome, D.cf, 
    case 
      when ((month(current_date) > month(D.dataDiNascita)) or
            (month(current_date) = month(D.dataDiNascita) and 
             day(current_date) >= day(D.dataDiNascita)))
      then year(current_date) - year(D.dataDiNascita)
      else year(current_date) - year(D.dataDiNascita) - 1
    end,  
    D.telefonoFisso, D.telefonoMobile, D.email, S.stipendio, 
    L.dataDiAssunzione, L.ruolo, L.cda
  from lavoro L, dipendente D, stipendio S
  where (L.dipendente = D.cf) and (S.dipendente = D.cf) and
        (S.inizio = (select max(S2.inizio)
                     from stipendio S2
                     where S2.dipendente = D.cf))
  order by L.teatro, D.cognome, D.nome                    
    
    
  create view statScena(spettacolo, paganti, affluenza, incasso) as
  select Sce.spettacolo, count(*), count(*) / Spa.capienza, sum(P.prezzo)
  from messaInScena Sce, spazio Spa, prenotazione P
  where (Sce.spazio = Spa.nome) and
        (P.dataSpettacolo = Sce.data) and
        (P.oraSpettacolo = Sce.ora) and
        (P.spazioSpettacolo = Sce.spazio)
  group by Sce.data, Sce.ora, Sce.spazio

  create view statSpettacolo(spettacolo, paganti, affluenza, incasso) as
  select Spe.titolo, avg(Sce.paganti), avg(Sce.affluenza), avg(Sce.incasso)
  from spettacolo Spe, statScena Sce
  where (Spe.titolo = Sce.spettacolo)
  group by Spe.titolo
  
}

Seguono alcune osservazioni:

Mostriamo infine l'implementazione delle transazioni tipiche di interrogazione (quelle di inserimento dipendono dai dati da inserire). Alcune di queste transazioni fanno uso delle viste definite nello schema:

Avanti Indietro Indice
Basi di dati - Massimo Franceschet