Second assignment.
This commit is contained in:
parent
796cbd7140
commit
4c4ffa048f
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,9 @@
|
||||
# ---> TeX
|
||||
out/
|
||||
|
||||
# SQLite database files
|
||||
*.db
|
||||
|
||||
## Core latex/pdflatex auxiliary files:
|
||||
*.aux
|
||||
*.lof
|
||||
|
@ -4,6 +4,7 @@
|
||||
\usepackage{enumitem}
|
||||
\usepackage{forest}
|
||||
\usepackage{graphicx}
|
||||
\usepackage[hidelinks]{hyperref}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{listings}
|
||||
\usepackage{pxfonts}
|
||||
@ -11,6 +12,8 @@
|
||||
\usepackage[normalem]{ulem}
|
||||
\usepackage{geometry}
|
||||
|
||||
\AfterEndEnvironment{figure}{\noindent}
|
||||
|
||||
\geometry{
|
||||
a4paper,
|
||||
top=30mm,
|
||||
@ -18,7 +21,13 @@ top=30mm,
|
||||
|
||||
\forestset{qtree/.style={for tree={parent anchor=south,
|
||||
child anchor=north,align=center,inner sep=0pt}}}
|
||||
\lstset{upquote=true,showstringspaces=false}
|
||||
\lstset{
|
||||
upquote=true,
|
||||
inputencoding=utf8,
|
||||
extendedchars=true,
|
||||
literate={à}{{\`a}}1, % Accept à in `lstlisting` environments
|
||||
showstringspaces=false
|
||||
}
|
||||
\lstdefinestyle{SQLu}{
|
||||
language=SQL,
|
||||
basicstyle=\small\ttfamily,
|
||||
@ -34,17 +43,22 @@ top=30mm,
|
||||
|
||||
|
||||
% First page information
|
||||
\title{\textbf{Basi di dati prof. Ghelli}\linebreak\textit{``Sempre sul pezzo"}}
|
||||
\title{\textbf{Basi di dati prof. Ghelli}\linebreak\textit{``Una cervecita fresca"}}
|
||||
\author{Davide Testa 613565}
|
||||
\date{2020-05-15}
|
||||
\date{2020-06-01}
|
||||
|
||||
\begin{document}
|
||||
\maketitle % Insert the title, author and date
|
||||
\input{\folder/source_code.tex}
|
||||
\section{Descrizione di massima del dominio (testo)}\label{sec:testo}
|
||||
\input{\folder/testo.tex}
|
||||
\clearpage
|
||||
\section{Descrizione del dominio}\label{sec:dominio}
|
||||
\input{\folder/dominio.tex}
|
||||
% \clearpage
|
||||
\section{Schema concettuale}\label{sec:schema-concettuale}
|
||||
La figura~\ref{fig:schema-concettuale} mostra lo schema concettuale in formato
|
||||
grafico.
|
||||
\begin{figure}[hb]
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{\folder/schema_concettuale.pdf}
|
||||
@ -52,11 +66,14 @@ top=30mm,
|
||||
\label{fig:schema-concettuale}
|
||||
\end{figure}
|
||||
\input{\folder/schema_concettuale.tex}
|
||||
\section{Schema logico}\label{sec:schema-logico}
|
||||
\clearpage
|
||||
\section{Schema logico relazionale}\label{sec:schema-logico}
|
||||
La figura~\ref{fig:schema-logico} mostra lo schema logico relazionale in
|
||||
formato grafico.\\
|
||||
\begin{figure}[htb]
|
||||
\centering
|
||||
\includegraphics[width=\linewidth]{\folder/schema_logico.pdf}
|
||||
\caption{Schema logico in formato grafico}
|
||||
\caption{Schema logico relazionale in formato grafico}
|
||||
\label{fig:schema-logico}
|
||||
\end{figure}
|
||||
\input{\folder/schema_logico.tex}
|
||||
@ -66,4 +83,6 @@ top=30mm,
|
||||
\vskip2pc
|
||||
\section{Piani di accesso}\label{sec:piani}
|
||||
\input{\folder/piani_di_accesso.tex}
|
||||
\vfill
|
||||
\input{\folder/source_code.tex}
|
||||
\end{document}
|
||||
|
@ -1,5 +1,75 @@
|
||||
% !TEX root = ../main.tex
|
||||
|
||||
Descrizione del dominio.
|
||||
L'applicazione ``Una cervecita fresca" deve fornire supporto ai birrai e alle
|
||||
birraie artigianali nella produzione delle loro birre fatte in casa con il
|
||||
metodo all-grain.
|
||||
|
||||
Nominare le classi di interesse, specificarne gli attributi e indicarne le relazioni con le altre classi.
|
||||
Ogni utente dell'app (birraio o birraia) può lavorare per uno o più birrifici, e può
|
||||
visualizzare le ricette dei birrifici per cui lavora.
|
||||
Del birraio o birraia sono rilevanti il nome, il cognome, il soprannome,
|
||||
l'indirizzo email, il codice fiscale.
|
||||
Ogni birrificio ha un nome, un anno di fondazione, un motto e uno stemma.
|
||||
Il birrificio ha inoltre una capacità produttiva, cioè il numero massimo di
|
||||
litri che può produrre in un singolo ciclo produttivo.
|
||||
Per ogni birrificio possono lavorare più persone.
|
||||
Ogni birrificio può lavorare a una sola produzione per volta, mettendo a
|
||||
disposizione tutta la sua capacità produttiva, o solo una parte di questa.
|
||||
|
||||
Ogni birrificio ha accesso a una o più ricette: ogni ricetta ha un nome, un
|
||||
creatore o creatrice, una data di creazione, una eventuale ricetta madre
|
||||
(ovvero la ricetta che è stata modificata per elaborarla) e la quantità
|
||||
relativa di ciascun ingrediente.
|
||||
Oltre alla creatrice o creatore di una ricetta, anche le birraie e i birrai di
|
||||
un birrificio possono vedere le ricette del birrificio in cui lavorano.
|
||||
La ricetta può essere archiviata o eliminata dal creatore o creatrice: lo stato
|
||||
della ricetta può essere attiva, archiviata, eliminata.
|
||||
|
||||
Gli ingredienti sono moltissimi, ma di solo 5 tipi: malti, luppoli, lieviti,
|
||||
zuccheri e additivi.
|
||||
Ciascun tipo ha un nome e la sua unità di misura appropriata (per esempio, i
|
||||
luppoli vengono espressi in \textit{mash}, ovvero grammi per litro di
|
||||
miscuglio, mentre i malti sono espressi in peso percentuale sugli ingredienti
|
||||
secchi).
|
||||
Ciascun ingrediente ha un tipo e una descrizione.
|
||||
In ogni ricetta, è indicata la quantità di ciascun ingrediente come numero
|
||||
puro: l'unità di misura (mash, peso percentuale, \textellipsis) dipende dal
|
||||
tipo di ingrediente.
|
||||
La quantità di acqua è ricavabile dagli altri ingredienti e non occorre quindi
|
||||
memorizzarla: si miscelano in proporzione gli ingredienti secchi, dal
|
||||
\textit{mash} si capisce quale volume finale deve raggiungere la soluzione.
|
||||
|
||||
La visualizzazione delle ricette terrà conto della quantità di prodotto che si
|
||||
vuole produrre (che dev'essere inferiore o uguale alla capacità produttiva del
|
||||
birrificio) per mostrare le quantità assolute dei vari ingredienti in kg e L;
|
||||
nel database invece le quantità verranno memorizzate in termini relativi come
|
||||
descritto sopra.
|
||||
|
||||
Una produzione è caratterizzata da una data di produzione, un numero di lotto
|
||||
che la identifica univocamente, il numero di bottiglie da 500 mL prodotte
|
||||
(questo è l'unico possibile formato di produzione) e uno stato di preparazione
|
||||
(in corso, completa, annullata).
|
||||
È prodotta seguendo una ricetta di un birrificio.
|
||||
|
||||
A ogni produzione si possono accompagnare alcune note.
|
||||
Ogni nota ha un testo.
|
||||
Esistono particolari note, dette di degustazione, che esprimono anche un
|
||||
giudizio da 1 a 10 sulla qualità del prodotto.
|
||||
|
||||
Il birrificio tiene un registro degli acquisti, conservando i dati della
|
||||
fattura e specificando per ogni ingrediente acquistato la quantità.
|
||||
Ogni fattura registrata dal birrificio è caratterizzata da una data, un numero
|
||||
di fattura, un importo e un fornitore.
|
||||
I fornitori hanno una ragione sociale, una partita IVA e un indirizzo.
|
||||
L'inventario mostra, per ogni ingrediente, la quantità disponibile e quella
|
||||
totale (compresi cioè gli ingredienti ``prenotati" da preparazioni in corso).
|
||||
|
||||
Oltre che come birrario o birraia, ci si può anche registrare come cliente,
|
||||
specificando un indirizzo di spedizione.
|
||||
I clienti sono caratterizzati, come chi produce birra, da nome, cognome, email
|
||||
e codice fiscale, ma non hanno un soprannome.
|
||||
|
||||
La clientela può effettuare prenotazioni per una quantità di bottiglie di un
|
||||
dato lotto di produzione.
|
||||
Ogni prenotazione ha uno stato, che rimane in sospeso fino al termine della
|
||||
produzione, quando il birrificio può impostarlo su `confermato' se intende
|
||||
procedere alla vendita oppure `annullato' se il prodotto non è soddisfacente.
|
||||
|
@ -3,33 +3,35 @@
|
||||
|
||||
\paragraph{Piano di accesso logico della query a}
|
||||
\begin{center}
|
||||
\begin{forest}
|
||||
[{$\pi^{b}$ R.C, S.D}
|
||||
[{$\bowtie$ R.E = S.F}
|
||||
[{$\sigma$ C $>=$ 10}
|
||||
[Tabella1 R]
|
||||
\begin{forest}, baseline, qtree
|
||||
[{$\pi^{b}$ r.IdRicetta , r.Nome}
|
||||
[{$\bowtie$ p.IdPersona = r.IdCreatrice}
|
||||
[{$\sigma$ p.Nome = 'Giovanni'}
|
||||
[Persone p]
|
||||
]
|
||||
[Tabella2 S]
|
||||
[Ricette r]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
|
||||
Non c'è in questo caso differenza tra $\pi^{b}$ e $\pi$: non ci possono essere
|
||||
duplicati.
|
||||
\paragraph{Piano di accesso fisico della query a senza indici}
|
||||
\begin{center}
|
||||
\begin{forest}
|
||||
[{Project(\{C, D\})}
|
||||
[{SortMerge(R.E = S.F)}
|
||||
[{Sort(\{E\})}
|
||||
[{Project(\{E, C\})}
|
||||
[{Filter(C $>=$ 10)}
|
||||
[{TableScan(Tabella R)}]
|
||||
\begin{forest}, baseline, qtree
|
||||
[{Project(\{r.IdRicetta , r.Nome\})}
|
||||
[{SortMerge(p.IdPersona = r.IdCreatrice)}
|
||||
[{Sort([p.IdPersona])}
|
||||
[{Project(\{p.IdPersona\})}
|
||||
[{Filter(p.Nome = 'Giovanni')}
|
||||
[{TableScan(Persone p)}]
|
||||
]
|
||||
]
|
||||
]
|
||||
[{Sort(\{F\})}
|
||||
[{Project(\{C, F\})}
|
||||
[{TableScan(Tabella S)}]
|
||||
[{Sort([r.IdCreatrice])}
|
||||
[{Project(\{r.IdRicetta, r.Nome, r.IdCreatrice\})}
|
||||
[{TableScan(Ricette r)}]
|
||||
]
|
||||
]
|
||||
]
|
||||
@ -40,12 +42,210 @@
|
||||
\paragraph{Piano di accesso fisico della query a con due indici}
|
||||
\begin{center}
|
||||
\begin{forest}, baseline, qtree
|
||||
[{Project(\{C, D\})}
|
||||
[{IndexNestedLoop(R.E = S.F)}
|
||||
[{IndexFilter(Tabella R,\\ IndRC, C $>=$ 10)}]
|
||||
[{IndexFilter(Tabella S,\\IndSF, S.F = R.E)}]
|
||||
[{Project(\{r.IdRicetta , r.Nome\})}
|
||||
[{IndexNestedLoop(p.IdPersona = r.IdCreatrice)}
|
||||
[{IndexFilter(Persone p,\\ IndPN, p.Nome = 'Giovanni')}]
|
||||
[{IndexFilter(Ricette r,\\IndRC, r.IdCreatrice = p.IdPersona)}]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
Indici necessari: \texttt{IndRC} (indice della tabella R sull’attributo C) e \texttt{IndSF} (indice della tabella S sull'attributo F).
|
||||
Indici necessari: \texttt{IndPN} (indice della tabella Persone sull’attributo
|
||||
Nome) e \texttt{IndRC} (indice della tabella Ricette sull'attributo Creatrice).
|
||||
|
||||
\clearpage
|
||||
\subsection{Query b}
|
||||
|
||||
\paragraph{Piano di accesso logico della query b}
|
||||
\begin{center}
|
||||
\begin{forest}, baseline, qtree
|
||||
[{$\tau$[-DiversiFornitori]}
|
||||
[{$\pi^{b}$ fa.IdBirrificio, COUNT(DISTINCT fa.IdFornitore) DiversiFornitori}
|
||||
[{$\sigma$ COUNT(DISTINCT fa.IdFornitore) $>=$ 3}
|
||||
[\{fa.IdBirrificio\} {$\gamma$ \{COUNT(DISTINCT fa.IdFornitore)\}}
|
||||
[{$\sigma$ fa.Data $>=$ '2020-01-01'}
|
||||
[Fatture fa]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
|
||||
Non c'è in questo caso differenza tra $\pi^{b}$ e $\pi$: non ci possono essere
|
||||
duplicati, in quanto la GROUP BY raggruppa per IdBirrificio.
|
||||
\paragraph{Piano di accesso fisico della query b senza indici}
|
||||
\begin{center}
|
||||
\begin{forest}, baseline, qtree
|
||||
[{Sort[-DiversiFornitori]}
|
||||
[{Project(\{fa.IdBirrificio, COUNT(DISTINCT fa.IdFornitore) DiversiFornitori\})}
|
||||
[{Filter(COUNT(DISTINCT fa.IdFornitore) $>=$ 3)}
|
||||
[{GroupBy(\{fa.IdBirrificio\}, \{COUNT(DISTINCT fa.IdFornitore)\})}
|
||||
[{Sort([fa.IdBirrificio])}
|
||||
[{Filter(fa.Data $>=$ '2020-01-01')}
|
||||
[{TableScan(Fatture fa)}]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
Il sort sull'attributo dimensione di analisi prima della GroupBy è necessario,
|
||||
in quanto non è garantito che i record della tabella Fatture siano raggruppati
|
||||
per IdBirrificio.
|
||||
Lo sarebbero se l'organizzazione primaria della tabella fosse sequenziale
|
||||
proprio su questo attributo, il che è estremamente poco probabile.
|
||||
\clearpage
|
||||
\paragraph{Piano di accesso fisico della query b con un indice}
|
||||
\begin{center}
|
||||
\begin{forest}, baseline, qtree
|
||||
[{Sort[-DiversiFornitori]}
|
||||
[{Project(\{fa.IdBirrificio, COUNT(DISTINCT fa.IdFornitore) DiversiFornitori\})}
|
||||
[{Filter(COUNT(DISTINCT fa.IdFornitore) $>=$ 3)}
|
||||
[{GroupBy(\{fa.IdBirrificio\}, \{COUNT(DISTINCT fa.IdFornitore)\})}
|
||||
[{Sort([fa.IdBirrificio])}
|
||||
[{IndexFilter(Fatture fa, IndFD, fa.Data $>=$ '2020-01-01')}]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
Indice necessario: \texttt{IndFD} (indice della tabella Fatture sull’attributo
|
||||
Data).
|
||||
Il sort sull'attributo IdBirrificio prima della GroupBy è necessario, in quanto
|
||||
i record in input sono ordinati per data, il che non ci garantisce che siano
|
||||
raggruppati per IdBirrificio (che è dimensione di analisi).
|
||||
|
||||
\subsection{Query c}
|
||||
|
||||
\paragraph{Piano di accesso logico della query c}
|
||||
\begin{center}
|
||||
\begin{forest}, baseline, qtree
|
||||
[{$\pi^{b}$ fo.RagioneSociale, SUM(fa.Importo) ImportoTotale, AVG(fa.Importo) ImportoMedio}
|
||||
[$\sigma$ SUM(fa.Importo) $>$ 10
|
||||
[{\{fo.IdFornitore, fo.RagioneSociale\} $\gamma$ \{SUM(fa.Importo), AVG(fa.Importo)\}}
|
||||
[{$\bowtie$ fa.IdFornitore = fo.IdFornitore}
|
||||
[{Fornitori fo}]
|
||||
[{$\bowtie$ fa.IdBirrificio = b.IdBirrificio}
|
||||
[{$\sigma$ b.Nome = 'Pirati Rossi'}
|
||||
[{Birrifici b}]
|
||||
]
|
||||
[{Fatture fa}]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
|
||||
In questo caso non ci dovrebbe essere differenza tra $\pi^{b}$ e $\pi$: non ci
|
||||
devono essere due fornitori con la stessa ragione sociale (la ragione sociale
|
||||
è chiave naturale); è comunque possibile un errore di inserimento se non ho
|
||||
impostato un vincolo di unicità anche su questo attributo, che non ho scelto
|
||||
come chiave primaria della tabella: ecco perché ho raggruppato anche per
|
||||
IdFornitore e non solo per RagioneSociale.
|
||||
|
||||
Ho scelto l'ordine di giunzione in modo da avere la restrizione il più distale
|
||||
possibile.
|
||||
|
||||
\clearpage
|
||||
|
||||
\paragraph{Piano di accesso fisico della query c senza indici}
|
||||
\begin{center}
|
||||
\begin{forest}, baseline, qtree
|
||||
[{Project(\{fo.RagioneSociale,\\SUM(fa.Importo) ImportoTotale, AVG(fa.Importo) ImportoMedio\})}
|
||||
[{Filter(SUM(fa.Importo) $>$ 10)}
|
||||
[{GroupBy(\{fo.IdFornitore,fo.RagioneSociale\}, \{SUM(fa.Importo), AVG(fa.Importo)\})}
|
||||
[{MergeSort(fa.IdFornitore = fo.IdFornitore)}
|
||||
[{Sort([fo.IdFornitore])}
|
||||
[{Project(\{fo.IdFornitore,\\fo.RagioneSociale\})}
|
||||
[{Fornitori fo}]
|
||||
]
|
||||
]
|
||||
[{Sort([fa.IdFornitore])}
|
||||
[{Project(\{fa.IdFornitore, fa.Importo\})}
|
||||
[{MergeSort(fa.IdBirrificio = b.IdBirrificio)}
|
||||
[{Sort([b.IdBirrificio])}
|
||||
[{Project(\{b.IdBirrificio\})}
|
||||
[{Filter(b.Nome = 'Pirati Rossi')}
|
||||
[{TableScan(Birrifici b)}]
|
||||
]
|
||||
]
|
||||
]
|
||||
[{Sort([fa.IdBirrificio])}
|
||||
[{Project(\{fa.IdBirrificio,\\fa.IdFornitore fa.Importo\})}
|
||||
[{Fatture fa}]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
Non è necessario ordinare per \texttt{[fo.IdFornitore, fo.RagioneSociale]} prima
|
||||
della GroupBy: per costruzione, l'ordine dell'operatore esterno della SortMerge
|
||||
viene mantenuto nell'output, e questo ordine è sull'attributo fo.IdFornitore,
|
||||
che a sua volta determina funzionalmente l'altra dimensione di analisi, fo.RagioneSociale.
|
||||
|
||||
Pertanto, è garantito che l'input della GroupBy sarà già raggruppato per gli
|
||||
attributi che sono dimensione di analisi e non occorre un ordinamento
|
||||
preventivo.
|
||||
|
||||
\clearpage
|
||||
\paragraph{Piano di accesso fisico della query c con tre indici}
|
||||
\begin{center}
|
||||
\begin{forest}, baseline, qtree
|
||||
[{Project(\{fo.RagioneSociale,\\SUM(fa.Importo) ImportoTotale, AVG(fa.Importo) ImportoMedio\})}
|
||||
[{Filter(SUM(fa.Importo) $>$ 10)}
|
||||
[{GroupBy(\{fo.IdFornitore, fo.RagioneSociale\},\\\{SUM(fa.Importo), AVG(fa.Importo)\})}
|
||||
[{Sorted([fo.IdFornitore])}
|
||||
[{Project(\{fo.IdFornitore, fo.RagioneSociale, fa.Importo\})}
|
||||
[{IndexNestedLoop(fa.IdFornitore = fo.IdFornitore)}
|
||||
[{IndexNestedLoop\\(fa.IdBirrificio = b.IdBirrificio)}
|
||||
[{IndexFilter(Birrifici b, IndBN,\\b.Nome = 'Pirati Rossi')}]
|
||||
[{IndexFilter(Fatture fa, IndFaIdB,\\fa.IdBirrificio = b.IdBirrificio)}]
|
||||
]
|
||||
[{IndexFilter(Fornitori fo, IndFoIdF,\\fo.IdFornitore = fa.IdFornitore)}]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
\end{forest}
|
||||
\end{center}
|
||||
Indici necessari: \texttt{IndBN} (indice della tabella Birrifici sull’attributo
|
||||
Nome), \texttt{IndFaIdB} (indice della tabella Fatture sull'attributo
|
||||
IdBirrificio) e \texttt{IndFoIdF} (indice della tabella Fornitori sull'attributo
|
||||
IdFornitore).
|
||||
|
||||
Occorre ordinare per IdFornitore prima della \texttt{GroupBy}, in quanto
|
||||
l'output della IndexNestedLoop è ordinato come l'operatore esterno, ovvero
|
||||
per nome del birrificio.
|
||||
|
||||
Potrei spostare l'ordinamento tra le due giunzioni con IndexNestedLoop, tanto
|
||||
ogni fattura ha un fornitore e l'output non andrà a decrecere dopo la seconda
|
||||
giunzione (anzi, si arricchirà di campi).
|
||||
Il sort andrebbe fatto con il minor numero possibile di dati, dato l'alto costo
|
||||
dell'algoritmo, eliminando i campi superflui con una project prima.
|
||||
|
||||
Il vantaggio dell'IndexNestedLoop sul SortMerge si ha solo se la condizione è
|
||||
sufficientemente restrittiva da essere soddisfatta da una piccola minoranza
|
||||
di record.
|
||||
In questo caso, la restrizione sul nome del birrificio dovrebbe essere
|
||||
abbastanza restrittiva (se ci sono abbastanza birrifici, il numero di
|
||||
birrifici con il nome `Pirati Rossi' sarà trascurabile rispetto al totale) ed
|
||||
è ragionevole che le fatture che riguardano quel birrificio siano una esigua
|
||||
minoranza rispetto al totale delle fatture.
|
||||
Se così non fosse, pur avendo i tre indici a disposizione, converrebbe
|
||||
utilizzare comunque il SortMerge.
|
||||
|
@ -1,14 +1,122 @@
|
||||
% !TEX root = ../main.tex
|
||||
|
||||
\begin{enumerate}[label=\alph*.]
|
||||
\item Uso di proiezione, join e restrizione
|
||||
\item Uso di proiezione, join e restrizione.
|
||||
|
||||
Per ogni record di R con valore di C maggiore o uguale a 10 e che ha un valore di E uguale a un valore di F nella tabella S, riportare R.C e S.D.
|
||||
\begin{lstlisting}[style=SQLu]
|
||||
SELECT R.C, S.D
|
||||
FROM Tabella1 R
|
||||
JOIN Tabella2 S ON R.E = S.F
|
||||
WHERE R.C >= 10
|
||||
Mostrare l'IdRicetta e il Nome delle ricette create da birrai di nome
|
||||
Giovanni.
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
SELECT r.IdRicetta, r.Nome
|
||||
FROM Ricette r
|
||||
JOIN Persone p ON p.IdPersona = r.IdCreatrice
|
||||
WHERE p.Nome = 'Giovanni'
|
||||
\end{lstlisting}
|
||||
\item Uso di group by con having, where e sort.
|
||||
|
||||
Per ogni birrificio che abbia fatto almeno un acquisto quest'anno,
|
||||
riportare l'IdBirrificio e il numero di diversi fornitori da cui ha
|
||||
acquistato quest'anno, se questo numero è almeno di 3.
|
||||
Ordinare il risultato dal birrificio che ha avuto più fornitori a quello
|
||||
che ne ha avuti meno.
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
SELECT fa.IdBirrificio,
|
||||
COUNT(DISTINCT fa.IdFornitore) DiversiFornitori
|
||||
FROM Fatture fa
|
||||
WHERE fa.Data >= '2020-01-01'
|
||||
GROUP BY fa.IdBirrificio
|
||||
HAVING COUNT(DISTINCT fa.IdFornitore) >= 3
|
||||
ORDER BY COUNT(DISTINCT fa.IdFornitore) DESC
|
||||
\end{lstlisting}
|
||||
\item Uso di join, group by con having e where.
|
||||
|
||||
Dei fornitori da cui ha ordinato il birrificio `Pirati Rossi', mostrare la
|
||||
ragione sociale, l'importo totale e l'importo medio delle fatture, purché
|
||||
l'importo totale sia superiore a 10 euro.
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
SELECT fo.RagioneSociale, SUM(fa.Importo) ImportoTotale,
|
||||
AVG(fa.Importo) ImportoMedio
|
||||
FROM Fornitori fo
|
||||
JOIN Fatture fa ON fa.IdFornitore = fo.IdFornitore
|
||||
JOIN Birrifici b ON b.IdBirrificio = fa.IdBirrificio
|
||||
WHERE b.Nome = 'Pirati Rossi'
|
||||
GROUP BY fo.IdFornitore, fo.RagioneSociale
|
||||
HAVING SUM(fa.Importo) > 10
|
||||
\end{lstlisting}
|
||||
\item Uso di select annidata con quantificazione esistenziale.
|
||||
|
||||
Mostrare il soprannome de* birrai* che siano aut*r* di almeno una ricetta.
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
SELECT b.Soprannome
|
||||
FROM Birraie b
|
||||
WHERE EXISTS (SELECT *
|
||||
FROM Ricette r
|
||||
WHERE r.IdCreatrice = b.IdPersona)
|
||||
\end{lstlisting}
|
||||
\clearpage
|
||||
|
||||
\item Uso di select annidata con quantificazione universale.
|
||||
|
||||
Mostrare il nome e il cognome de* clienti che hanno ordinato da un solo
|
||||
birrificio.
|
||||
|
||||
\textbf{Traduco in notazione insiemistica:}
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
{p1.Nome, p1.Cognome | (p1 @$\in$@ Persone, pre1 @$\in$@ Prenotazioni,
|
||||
pre1.IdCliente = p1.IdPersona,
|
||||
pro1 @$\in$@ Produzioni,
|
||||
pro1.IdProduzione = pre1.IdProduzione,
|
||||
r1 @$\in$@ Ricette,
|
||||
r1.IdRicetta = pro1.IdRicetta) .
|
||||
@$\forall$@ (pre2 @$\in$@ Prenotazioni, pre2.IdCliente = pre1.IdCliente
|
||||
pro2 @$\in$@ Produzioni, pro2.IdProduzione = pre2.IdProduzione,
|
||||
r2 @$\in$@ Ricette, r2.IdRicetta = pro2.IdRicetta) .
|
||||
(r2.IdBirrificio = r1.IdBirrificio)}
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Sostituisco il $\forall x . P$ con $\neg\exists x . \neg P$}
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
{p1.Nome, p1.Cognome | (p1 @$\in$@ Persone, pre1 @$\in$@ Prenotazioni,
|
||||
pre1.IdCliente = p1.IdPersona,
|
||||
pro1 @$\in$@ Produzioni,
|
||||
pro1.IdProduzione = pre1.IdProduzione,
|
||||
r1 @$\in$@ Ricette,
|
||||
r1.IdRicetta = pro1.IdRicetta) .
|
||||
@$\neg\exists$@ (pre2 @$\in$@ Prenotazioni, pre2.IdCliente = pre1.IdCliente
|
||||
pro2 @$\in$@ Produzioni, pro2.IdProduzione = pre2.IdProduzione,
|
||||
r2 @$\in$@ Ricette, r2.IdRicetta = pro2.IdRicetta) .
|
||||
(r2.IdBirrificio @$\neq$@ r1.IdBirrificio)}
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Scrivo quindi la query}, inserendo l'IdPersona e la parola chiave
|
||||
\texttt{DISTINCT} per rimuovere i duplicati (ma non le persone omonime).
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
SELECT DISTINCT p1.IdPersona, p1.Nome, p1.Cognome
|
||||
FROM Persone p1
|
||||
JOIN Prenotazioni pre1 ON pre1.IdCliente = p1.IdPersona
|
||||
JOIN Produzioni pro1 ON pro1.IdProduzione = pre1.IdProduzione
|
||||
JOIN Ricette r1 ON r1.IdRicetta = pro1.IdRicetta
|
||||
WHERE NOT EXISTS (SELECT *
|
||||
FROM Prenotazioni pre2
|
||||
JOIN Produzioni pro2
|
||||
ON pro2.IdProduzione = pre2.IdProduzione
|
||||
JOIN Ricette r2 ON r2.IdRicetta = pro2.IdRicetta
|
||||
WHERE pre2.IdCliente = pre1.IdCliente
|
||||
AND r2.IdBirrificio <> r1.IdBirrificio)
|
||||
\end{lstlisting}
|
||||
|
||||
\item Uso di subquery di confronto quantificato.
|
||||
|
||||
Per ogni birrificio, mostrare l'IdBirrificio e l'ultimo NumeroLotto
|
||||
prodotto in quel birrificio (sapendo che il NumeroLotto è progressivo).
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
SELECT r1.IdBirrificio, pro1.NumeroLotto
|
||||
FROM Produzioni pro1
|
||||
JOIN Ricette r1 ON r1.IdRicetta = pro1.IdRicetta
|
||||
WHERE pro1.NumeroLotto >= ANY (SELECT pro2.NumeroLotto
|
||||
FROM Produzioni pro2
|
||||
JOIN Ricette r2
|
||||
ON r2.IdRicetta = pro2.IdRicetta
|
||||
WHERE r2.IdBirrificio = r1.IdBirrificio)
|
||||
\end{lstlisting}
|
||||
\end{enumerate}
|
||||
\clearpage
|
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1,3 +1,100 @@
|
||||
% !TEX root = ../main.tex
|
||||
|
||||
Vincoli non catturati graficamente: \textellipsis
|
||||
\paragraph{Elenco degli ingredienti disponibili}
|
||||
*l* committente ha chiesto espressamente l'elenco degli ingredienti disponibili.
|
||||
Tuttavia, questo è calcolabile a partire dagli acquisti e dalle produzioni.
|
||||
Ho convinto *l* committente a non memorizzare separatamente l'inventario degli
|
||||
ingredienti, rassicurandol* che avrei fornito una vista logica ``Inventario".
|
||||
|
||||
\begin{lstlisting}[style=SQLu][float,floatplacement=H]
|
||||
CREATE VIEW IngredientiAcquistatiTotali (IdIngrediente, Ingrediente,
|
||||
Totale)
|
||||
AS SELECT i.IdIngrediente IdIngrediente, i.Descrizione Ingrediente,
|
||||
SUM(a.Quantità) Totale
|
||||
FROM Acquisti a
|
||||
JOIN Ingredienti i ON i.IdIngrediente = a.IdIngrediente
|
||||
GROUP BY i.IdIngrediente, i.Descrizione;
|
||||
|
||||
CREATE VIEW IngredientiInUso (IdIngrediente, Ingrediente, InUso)
|
||||
AS SELECT i.IdIngrediente IdIngrediente, i.Descrizione Ingrediente,
|
||||
SUM(ir.Quantità) InUso
|
||||
FROM IngredientiRicette ir
|
||||
JOIN Ingredienti i ON i.IdIngrediente = ir.IdIngrediente
|
||||
JOIN Produzioni p ON p.IdRicetta = ir.IdRicetta
|
||||
WHERE p.Stato IS NULL
|
||||
GROUP BY i.IdIngrediente, i.Descrizione;
|
||||
|
||||
CREATE VIEW IngredientiUsati (IdIngrediente, Ingrediente,
|
||||
Usati)
|
||||
AS SELECT i.IdIngrediente IdIngrediente, i.Descrizione Ingrediente,
|
||||
SUM(ir.Quantità) Usati
|
||||
FROM IngredientiRicette ir
|
||||
JOIN Ingredienti i ON i.IdIngrediente = ir.IdIngrediente
|
||||
JOIN Produzioni p ON p.IdRicetta = ir.IdRicetta
|
||||
WHERE p.Stato = 0
|
||||
GROUP BY i.IdIngrediente, i.Descrizione;
|
||||
|
||||
CREATE VIEW Inventario (IdIngrediente, Ingrediente, QuantitàTotale,
|
||||
QuantitàDisponibile)
|
||||
AS SELECT iat.IdIngrediente IdIngrediente,
|
||||
iat.Ingrediente Ingrediente,
|
||||
(iat.Totale - COALESCE(iu.Usati, 0)) QuantitàTotale,
|
||||
(iat.Totale - COALESCE(iu.Usati, 0)
|
||||
- COALESCE(iiu.InUso, 0)) QuantitàDisponibile
|
||||
FROM IngredientiAcquistatiTotali iat
|
||||
LEFT JOIN IngredientiUsati iu
|
||||
ON iu.idIngrediente = iat.IdIngrediente
|
||||
LEFT JOIN IngredientiInUso iiu
|
||||
ON iiu.idIngrediente = iat.IdIngrediente
|
||||
WHERE iat.Totale - COALESCE(iu.Usati, 0) > 0;
|
||||
\end{lstlisting}
|
||||
|
||||
Ogni volta che si inizia una produzione, l'applicazione controlla che la
|
||||
quantità di ingredienti disponibili superi la quantità degli ingredienti
|
||||
necessari alla preparazione.
|
||||
|
||||
L'applicazione può anche mostrare una ``lista della spesa" basandosi su ricette
|
||||
che si vogliono preparare e sulla vista inventario.
|
||||
|
||||
L'applicazione mostrerà ad ogni birrai* solo le ricette di cui è aut*r* o di un
|
||||
birrificio per cui lavora.
|
||||
|
||||
\begin{minipage}{\linewidth}
|
||||
\paragraph{Vincoli intra-relazionali}
|
||||
\begin{itemize}
|
||||
\itemsep0em
|
||||
\item Non possono esistere due persone con lo stesso codice fiscale.
|
||||
\item Non possono esistere due birrai* con lo stesso soprannome.
|
||||
\item Non possono esistere due fornitori con la stessa partita IVA né
|
||||
con la stessa ragione sociale.
|
||||
\item Il tipo di ingrediente determina l'unità di misura.
|
||||
Esiste un breve elenco di tipi ingredienti disponibili con la relativa unità di
|
||||
misura.
|
||||
Non ho creato una classe ``TipiIngredienti" per contenere il numero di classi,
|
||||
ma in effetti il tipo determina funzionalmente l'unità di misura ed esistono
|
||||
pochi tipi, mentre ci sono molti ingredienti per ogni tipo.
|
||||
\end{itemize}
|
||||
\paragraph{Vincoli inter-relazionali}
|
||||
\begin{itemize}
|
||||
\itemsep0em
|
||||
\item Alla registrazione, l'utente deve inserire un soprannome e/o un indirizzo
|
||||
di spedizione: il vincolo di copertura impone che l'unione di Clienti e
|
||||
Birraie sia Persone, non devono esistere persone che non sono né clienti
|
||||
né birrai*.
|
||||
\item In ogni produzione, il NumeroBottiglie diviso per 0.5 non deve superare la
|
||||
CapacitàProduttiva del birrificio.
|
||||
\item Ogni produzione deve iniziare con stato `in corso';
|
||||
non può iniziare una produzione se un altra è `in corso' nello stesso
|
||||
birrificio.
|
||||
\item Ogni nota deve fare riferimento a una produzione.
|
||||
\item Ogni prenotazione deve fare riferimento a una produzione.
|
||||
\item Ogni prenotazione deve fare riferimento ad un* cliente.
|
||||
\item Ogni produzione deve seguire una ricetta.
|
||||
\item Due produzioni di uno stesso birrificio non devono avere lo stesso lotto.
|
||||
\item Ogni ricetta deve avere un* creat*r* e un birrificio di riferimento.
|
||||
\item Ogni fattura deve fare riferimento ad un birrificio e un fornitore.
|
||||
\item Ogni acquisto deve riferirsi a una fattura e un ingrediente.
|
||||
\item Ogni ricetta deve avere almeno un ingrediente per ciascuno dei
|
||||
seguenti tipi: malto, luppolo, lievito.
|
||||
\end{itemize}
|
||||
\end{minipage}
|
||||
|
Binary file not shown.
@ -1,21 +1,63 @@
|
||||
% !TEX root = ../main.tex
|
||||
|
||||
\\
|
||||
\textbf{Schema logico relazionale in formato testuale}
|
||||
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
Tabella(@\underline{ChiavePrimaria}@, B, C, D)
|
||||
AltraTabella(@\underline{ChiavePrimariaEdEsterna*}@, E)
|
||||
@\textellipsis@
|
||||
Persone(@\underline{IdPersona}@, Nome, Cognome, Email, CodiceFiscale)
|
||||
Clienti(@\underline{IdPersona*}@, IndirizzoSpedizione)
|
||||
Birraie(@\underline{IdPersona*}@, Soprannome)
|
||||
Birrifici(@\underline{IdBirrificio}@, Nome, AnnoFondazione, Motto, Stemma,
|
||||
CapacitàProduttiva)
|
||||
BirrificiBirraie(@\underline{IdBirrificio*, IdBirraia*}@)
|
||||
Fornitori(@\underline{IdFornitore}@, RagioneSociale, PartitaIva, Indirizzo)
|
||||
Fatture(@\underline{IdFattura}@, IdBirrificio*, IdFornitore*, Data,
|
||||
NumeroFattura, Importo)
|
||||
TipiIngredienti(@\underline{IdTipo}@, Tipo, UnitàDiMisura)
|
||||
Ingredienti(@\underline{IdIngrediente}@, IdTipo*, Descrizione)
|
||||
Acquisti(@\underline{IdFattura*, IdIngrediente*}@, Quantità)
|
||||
Ricette(@\underline{IdRicetta}@, IdBirrificio*, IdCreatrice*, IdRicettaMadre*,
|
||||
Nome, DataCreazione, Stato)
|
||||
IngredientiRicette(@\underline{IdRicetta*, IdIngrediente*}@, Quantità)
|
||||
Produzioni(@\underline{IdProduzione}@, IdRicetta*, DataProduzione, NumeroLotto,
|
||||
Stato, NumeroBottiglie)
|
||||
Prenotazioni(@\underline{IdCliente*, IdProduzione*}@, Stato, Quantità)
|
||||
Note(@\underline{IdNota}@, IdProduzione*, Testo)
|
||||
NoteDegustazione(@\underline{IdNota*}@, Giudizio)
|
||||
\end{lstlisting}
|
||||
|
||||
\paragraph{Dipendenze funzionali}
|
||||
\begin{itemize}
|
||||
\item Per ogni tabella la chiave primaria (sottolineata) determina ciascuno degli attributi della tabella
|
||||
\item Altre eventuali dipendenze
|
||||
\itemsep0em
|
||||
\item Per ogni tabella la chiave primaria (sottolineata) determina ciascuno
|
||||
degli attributi della tabella.
|
||||
\begin{lstlisting}[style=SQLu,escapechar=@]
|
||||
IdPersona @$\to$@ Nome, IdPersona @$\to$@ Cognome, IdPersona @$\to$@ Email,
|
||||
IdPersona @$\to$@ CodiceFiscale, @$\textellipsis$@
|
||||
\end{lstlisting}
|
||||
\item Nella tabella \texttt{Persone}, \texttt{CodiceFiscale} è chiave
|
||||
naturale e determina tutti gli altri attributi.
|
||||
Ho ritenuto prudente aggiungere una chiave artificiale perché, se è vero
|
||||
che due persone diverse non avranno mai lo stesso codice fiscale, è vero
|
||||
anche che ci possono essere errori umani nell'inserimento di un CF e voglio
|
||||
riservarmi la possibilità di correggere un CF senza minare l'affidabiltà
|
||||
della base di dati.
|
||||
\item Stesso discorso per la RagioneSociale e la PartitaIva nella tabella
|
||||
\texttt{Fornitori}: ciascuno è chiave separatamente.
|
||||
\item Nella tabella \texttt{Fatture}, la coppia di attributi \texttt{\{IdFornitore, NumeroFattura\}}
|
||||
è chiave.
|
||||
\item Nella tabella \texttt{Produzioni}, il NumeroLotto \underline{non} è chiave, in
|
||||
quanto birrifici diversi possono avere lotti uguali, è solo all'interno del
|
||||
birrificio che il lotto identifica univocamente la produzione.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
Uno schema R, avente insieme di attributi T e insieme di dipendenze funzionali F, (\lstinline{R<T, F>}) è
|
||||
in forma normale di Boyce-Codd (BCNF) se ogni dipendenza funzionale della chiusura di F o è
|
||||
banale o ha come determinante una superchiave di T.
|
||||
Esiste un teorema che semplifica il calcolo, asserendo che se la condizione di cui sopra vale per
|
||||
una qualsiasi copertura di F allora vale per l’intera chiusura di F.
|
||||
|
||||
Nella copertura di F che ho descritto sopra (che peraltro è canonica: ogni dipendenza ha un
|
||||
solo attributo come determinato, nessuna dipendenza è ridondante e non sono presenti
|
||||
attributi estranei, in quanto ogni determinante è chiave), ogni dipendenza funzionale ha
|
||||
come determinante o la chiave primaria o una chiave naturale che non è stata scelta come
|
||||
primaria, in ogni caso una superchiave. \underline{La BCNF è pertanto rispettata}.
|
||||
|
5
compitino/secondo_compitino/source_code.tex
Normal file
5
compitino/secondo_compitino/source_code.tex
Normal file
@ -0,0 +1,5 @@
|
||||
% !TEX root = ../main.tex
|
||||
|
||||
\begin{center}
|
||||
Codice sorgente e test: \href{https://gogs.davte.it/Davte/basi_di_dati}{https://gogs.davte.it/Davte/basi\_di\_dati}
|
||||
\end{center}
|
37
compitino/secondo_compitino/test/data.sql
Normal file
37
compitino/secondo_compitino/test/data.sql
Normal file
File diff suppressed because one or more lines are too long
228
compitino/secondo_compitino/test/data_and_schema.sql
Normal file
228
compitino/secondo_compitino/test/data_and_schema.sql
Normal file
File diff suppressed because one or more lines are too long
192
compitino/secondo_compitino/test/schema.sql
Normal file
192
compitino/secondo_compitino/test/schema.sql
Normal file
@ -0,0 +1,192 @@
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE IF NOT EXISTS "Acquisti" (
|
||||
"IdFattura" INTEGER NOT NULL,
|
||||
"IdIngrediente" INTEGER NOT NULL,
|
||||
"Quantità" INTEGER NOT NULL,
|
||||
PRIMARY KEY("IdFattura","IdIngrediente")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Birraie" (
|
||||
"IdPersona" INTEGER NOT NULL,
|
||||
"Soprannome" TEXT NOT NULL UNIQUE,
|
||||
PRIMARY KEY("IdPersona")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Birrifici" (
|
||||
"IdBirrificio" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"Nome" TEXT,
|
||||
"AnnoFondazione" INTEGER,
|
||||
"Motto" TEXT,
|
||||
"Stemma" BLOB,
|
||||
"CapacitàProduttiva" INTEGER NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "BirrificiBirraie" (
|
||||
"IdBirrificio" INTEGER NOT NULL,
|
||||
"IdBirraia" INTEGER NOT NULL,
|
||||
PRIMARY KEY("IdBirrificio","IdBirraia")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Clienti" (
|
||||
"IdPersona" INTEGER NOT NULL,
|
||||
"IndirizzoSpedizione" TEXT NOT NULL,
|
||||
PRIMARY KEY("IdPersona")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Fatture" (
|
||||
"IdFattura" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"IdBirrificio" INTEGER NOT NULL,
|
||||
"IdFornitore" INTEGER NOT NULL,
|
||||
"Data" TEXT NOT NULL,
|
||||
"NumeroFattura" INTEGER NOT NULL UNIQUE,
|
||||
"Importo" INTEGER NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Fornitori" (
|
||||
"IdFornitore" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"RagioneSociale" TEXT NOT NULL UNIQUE,
|
||||
"PartitaIva" INTEGER NOT NULL UNIQUE,
|
||||
"Indirizzo" TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Ingredienti" (
|
||||
"IdIngrediente" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"IdTipo" INTEGER NOT NULL,
|
||||
"Descrizione" TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "IngredientiRicette" (
|
||||
"IdRicetta" INTEGER NOT NULL,
|
||||
"IdIngrediente" INTEGER NOT NULL,
|
||||
"Quantità" INTEGER NOT NULL,
|
||||
PRIMARY KEY("IdRicetta","IdIngrediente")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Note" (
|
||||
"IdNota" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"IdProduzione" INTEGER NOT NULL,
|
||||
"Testo" TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "NoteDegustazione" (
|
||||
"IdNota" INTEGER NOT NULL,
|
||||
"Giudizio" INTEGER NOT NULL,
|
||||
PRIMARY KEY("IdNota")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Persone" (
|
||||
"IdPersona" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"Nome" TEXT NOT NULL,
|
||||
"Cognome" TEXT NOT NULL,
|
||||
"Email" TEXT NOT NULL,
|
||||
"CodiceFiscale" TEXT NOT NULL UNIQUE
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Prenotazioni" (
|
||||
"IdCliente" INTEGER NOT NULL,
|
||||
"IdProduzione" INTEGER NOT NULL,
|
||||
"Stato" INTEGER,
|
||||
"Quantità" INTEGER NOT NULL,
|
||||
PRIMARY KEY("IdProduzione","IdCliente")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Produzioni" (
|
||||
"IdProduzione" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"IdRicetta" INTEGER NOT NULL,
|
||||
"DataProduzione" TEXT NOT NULL,
|
||||
"NumeroLotto" INTEGER NOT NULL,
|
||||
"Stato" INTEGER,
|
||||
"NumeroBottiglie" INTEGER NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "Ricette" (
|
||||
"IdRicetta" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"IdBirrificio" INTEGER NOT NULL,
|
||||
"IdCreatrice" INTEGER NOT NULL,
|
||||
"IdRicettaMadre" INTEGER,
|
||||
"Nome" TEXT NOT NULL,
|
||||
"DataCreazione" TEXT NOT NULL,
|
||||
"Stato" INTEGER
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "TipiIngredienti" (
|
||||
"IdTipo" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"Tipo" TEXT NOT NULL,
|
||||
"UnitàDiMisura" TEXT NOT NULL
|
||||
);
|
||||
CREATE VIEW Query_a
|
||||
AS SELECT r.IdRicetta, r.Nome
|
||||
FROM Ricette r
|
||||
JOIN Persone p ON p.IdPersona = r.IdCreatrice
|
||||
WHERE p.Nome = 'Giovanni';
|
||||
CREATE VIEW Query_b
|
||||
AS SELECT fa.IdBirrificio,
|
||||
COUNT(DISTINCT fa.IdFornitore) DiversiFornitori
|
||||
FROM Fatture fa
|
||||
WHERE fa.Data >= '2020-01-01'
|
||||
GROUP BY fa.IdBirrificio
|
||||
HAVING COUNT(DISTINCT fa.IdFornitore) >= 3
|
||||
ORDER BY COUNT(DISTINCT fa.IdFornitore) DESC;
|
||||
CREATE VIEW Query_c
|
||||
AS SELECT fo.RagioneSociale, SUM(fa.Importo) ImportoTotale,
|
||||
AVG(fa.Importo) ImportoMedio
|
||||
FROM Fornitori fo
|
||||
JOIN Fatture fa ON fa.IdFornitore = fo.IdFornitore
|
||||
JOIN Birrifici b ON b.IdBirrificio = fa.IdBirrificio
|
||||
WHERE b.Nome = 'Pirati Rossi'
|
||||
GROUP BY fo.IdFornitore, fo.RagioneSociale
|
||||
HAVING SUM(fa.Importo) > 10;
|
||||
CREATE VIEW Query_d
|
||||
AS SELECT b.Soprannome
|
||||
FROM Birraie b
|
||||
WHERE EXISTS (SELECT *
|
||||
FROM Ricette r
|
||||
WHERE r.IdCreatrice = b.IdPersona);
|
||||
CREATE VIEW Query_e
|
||||
AS SELECT DISTINCT p1.IdPersona, p1.Nome, p1.Cognome
|
||||
FROM Persone p1
|
||||
JOIN Prenotazioni pre1
|
||||
ON pre1.IdCliente = p1.IdPersona
|
||||
JOIN Produzioni pro1
|
||||
ON pro1.IdProduzione = pre1.IdProduzione
|
||||
JOIN Ricette r1
|
||||
ON r1.IdRicetta = pro1.IdRicetta
|
||||
WHERE NOT EXISTS (SELECT *
|
||||
FROM Prenotazioni pre2
|
||||
JOIN Produzioni pro2
|
||||
ON pro2.IdProduzione = pre2.IdProduzione
|
||||
JOIN Ricette r2 ON r2.IdRicetta = pro2.IdRicetta
|
||||
WHERE pre2.IdCliente = pre1.IdCliente
|
||||
AND r2.IdBirrificio <> r1.IdBirrificio);
|
||||
CREATE VIEW Query_f
|
||||
AS SELECT r1.IdBirrificio, pro1.NumeroLotto
|
||||
FROM Produzioni pro1
|
||||
JOIN Ricette r1 ON r1.IdRicetta = pro1.IdRicetta
|
||||
WHERE pro1.NumeroLotto = (SELECT MAX(pro2.NumeroLotto)
|
||||
FROM Produzioni pro2
|
||||
JOIN Ricette r2
|
||||
ON r2.IdRicetta = pro2.IdRicetta
|
||||
WHERE r2.IdBirrificio = r1.IdBirrificio);
|
||||
CREATE VIEW IngredientiUsati (IdIngrediente, Ingrediente,
|
||||
Usati)
|
||||
AS SELECT i.IdIngrediente IdIngrediente, i.Descrizione Ingrediente,
|
||||
SUM(ir.Quantità) Usati
|
||||
FROM IngredientiRicette ir
|
||||
JOIN Ingredienti i ON i.IdIngrediente = ir.IdIngrediente
|
||||
JOIN Produzioni p ON p.IdRicetta = ir.IdRicetta
|
||||
WHERE p.Stato = 0
|
||||
GROUP BY i.IdIngrediente, i.Descrizione;
|
||||
CREATE VIEW IngredientiInUso (IdIngrediente, Ingrediente, InUso)
|
||||
AS SELECT i.IdIngrediente IdIngrediente, i.Descrizione Ingrediente,
|
||||
SUM(ir.Quantità) InUso
|
||||
FROM IngredientiRicette ir
|
||||
JOIN Ingredienti i ON i.IdIngrediente = ir.IdIngrediente
|
||||
JOIN Produzioni p ON p.IdRicetta = ir.IdRicetta
|
||||
WHERE p.Stato IS NULL
|
||||
GROUP BY i.IdIngrediente, i.Descrizione;
|
||||
CREATE VIEW IngredientiAcquistatiTotali (IdIngrediente, Ingrediente,
|
||||
Totale)
|
||||
AS SELECT i.IdIngrediente IdIngrediente, i.Descrizione Ingrediente,
|
||||
SUM(a.Quantità) Totale
|
||||
FROM Acquisti a
|
||||
JOIN Ingredienti i ON i.IdIngrediente = a.IdIngrediente
|
||||
GROUP BY i.IdIngrediente, i.Descrizione;
|
||||
CREATE VIEW Inventario (IdIngrediente, Ingrediente, QuantitàTotale,
|
||||
QuantitàDisponibile)
|
||||
AS SELECT iat.IdIngrediente IdIngrediente,
|
||||
iat.Ingrediente Ingrediente,
|
||||
(iat.Totale - COALESCE(iu.Usati, 0)) QuantitàTotale,
|
||||
(iat.Totale - COALESCE(iu.Usati, 0)
|
||||
- COALESCE(iiu.InUso, 0)) QuantitàDisponibile
|
||||
FROM IngredientiAcquistatiTotali iat
|
||||
LEFT JOIN IngredientiUsati iu
|
||||
ON iu.idIngrediente = iat.IdIngrediente
|
||||
LEFT JOIN IngredientiInUso iiu
|
||||
ON iiu.idIngrediente = iat.IdIngrediente
|
||||
WHERE iat.Totale - COALESCE(iu.Usati, 0) > 0;
|
||||
COMMIT;
|
@ -1,3 +1,88 @@
|
||||
% !TEX root = ../main.tex
|
||||
|
||||
Il testo viene assegnato dal Professore.
|
||||
\paragraph{Introduzione}
|
||||
La birra fatta in casa è un'attività che riceve crescente attenzione da parte degli appassionati.
|
||||
Ogni birraio amatoriale possiede un'attrezzatura per il processo di produzione della birra su
|
||||
piccola scala (bollitori, fermentatori, tubi, ecc.) con una certa capacità massima di
|
||||
fermentazione: il numero di litri che l'attrezzatura è in grado di gestire in un unico ``lotto". La
|
||||
preparazione della birra richiede anche ingredienti, le cui quantità effettive variano da una
|
||||
ricetta all'altra, questi sono vari tipi di malto, luppolo, lieviti e zuccheri (e, naturalmente, acqua).
|
||||
|
||||
Ai birrai piace registrare le proprie ricette per riferimento futuro e mantenere un elenco
|
||||
aggiornato degli ingredienti disponibili per fare acquisti prima della successiva produzione.
|
||||
|
||||
L'obiettivo di questo progetto è quello di sviluppare un'applicazione per i birrai domestici che
|
||||
consenta loro di mantenere un elenco di ricette e adattare quelle esistenti. L'applicazione deve
|
||||
anche:
|
||||
\begin{itemize}
|
||||
\itemsep0em
|
||||
\item mantenere un elenco di ingredienti disponibili;
|
||||
\item aggiornare questo elenco dopo un ciclo di produzione e quando vengono acquistati nuovi ingredienti;
|
||||
\item produrre liste della spesa per il lotto successivo;
|
||||
\item guidare il birraio nel processo di produzione.
|
||||
\end{itemize}
|
||||
|
||||
\paragraph{Descrizione del progetto}
|
||||
``Una cervecita fresca" è un'applicazione che consente ai produttori amatoriali di birra di
|
||||
mantenere un database organizzato delle loro ricette di birra. L'applicazione consente agli
|
||||
utenti di creare, archiviare e modificare ricette, e successivamente eliminarle, se l'utente
|
||||
desidera farlo. L'applicazione è destinata solo ai produttori di birra con metodo
|
||||
\href{https://www.birradegliamici.com/fare-la-birra/all-grain/}{all-grain}, e
|
||||
quindi tutte le ricette sono per questo tipo di birre (le birre ``estratto" non
|
||||
sono supportate).
|
||||
|
||||
Ogni birrificio domestico dispone di un'attrezzatura specifica, le cui caratteristiche portano a
|
||||
una particolare ``dimensione del lotto": il numero massimo di litri che possono essere prodotti
|
||||
in una singola produzione.
|
||||
Le ricette prevedono, oltre all'acqua:
|
||||
|
||||
\begin{itemize}
|
||||
\itemsep0em
|
||||
\item malti
|
||||
\item luppolo
|
||||
\item lieviti
|
||||
\item zuccheri
|
||||
\item additivi
|
||||
\end{itemize}
|
||||
|
||||
Mentre i produttori di birra preferiscono creare ricette riferendosi a valori concreti, come
|
||||
chilogrammi di un particolare malto o grammi di un particolare luppolo, l'applicazione deve
|
||||
memorizzare queste ricette in una misura ``assoluta", che consente una conversione diretta
|
||||
della ricetta quando l'apparecchiatura, e di conseguenza la dimensione del lotto, è diversa.
|
||||
Ad esempio, una possibilità è esprimere la quantità di malto in percentuale del totale e usare
|
||||
i grammi per litro di miscuglio (mash) per il luppolo.
|
||||
|
||||
Oltre alle ricette, l'applicazione deve conservare le \textbf{istanze} della ricetta, ovvero singole
|
||||
produzioni basate su una ricetta; queste istanze possono essere accompagnate da note per
|
||||
fare riferimento a problemi che possono influire sulla birra risultante, note che i produttori di
|
||||
birra vorrebbero rimanessero memorizzate. Un particolare tipo di nota sono le note di
|
||||
degustazione, che consentono ai birrai di tenere traccia delle opinioni su una birra di un dato
|
||||
lotto.
|
||||
|
||||
Oltre a queste funzionalità più tradizionali, l'applicazione “Una cervecita fresca”, mantiene un
|
||||
elenco di ingredienti disponibili. Ciò consente ai birrai di avere la lista degli ingredienti
|
||||
mancanti per la prossima produzione. Un'istanza della ricetta, ovvero una produzione di birra,
|
||||
dovrebbe consentire agli utenti di aggiornare l'elenco degli ingredienti disponibili, sottraendo
|
||||
gli ingredienti usati da quelli disponibili.
|
||||
|
||||
Sarà inoltre possibile per i birrai vendere la birra prodotta. L’applicazione deve offrire
|
||||
un’interfaccia web per la prenotazione e la vendita. Un cliente registrato può prenotare un lotto
|
||||
di birra in produzione, oppure parte di esso. Quando il lotto è stato prodotto, il birraio può
|
||||
confermare le prenotazioni e procedere con la vendita oppure, se non è soddisfatto del
|
||||
prodotto, cancellarle, per non danneggiare il proprio buon nome. La birra non prenotata può
|
||||
essere messa in vendita e comprata da utenti registrati.
|
||||
|
||||
\paragraph{Scopo dell’applicazione}
|
||||
Il sistema deve implementare le funzionalità sopra descritte, ovvero creazione, modifica e
|
||||
cancellazione di ricette, creazione di istanze di ricette (birre), supporto per le note sulle birre,
|
||||
controllo degli ingredienti disponibili, supporto alla produzione con allarmi, supporto alla
|
||||
vendita.
|
||||
|
||||
\paragraph{Scopo del progetto per quanto riguarda Basi di Dati}
|
||||
Si integrano i requisiti già specificati con le seguenti ulteriori informazioni:
|
||||
\begin{itemize}
|
||||
\itemsep0em
|
||||
\item le ricette sono relative ad un solo birrificio ma possono essere condivise tra
|
||||
diversi birrai che sono autorizzati al loro utilizzo;
|
||||
\item gli ingredienti possono essere acquistati da più fornitori (registrati).
|
||||
\end{itemize}
|
||||
|
Loading…
x
Reference in New Issue
Block a user