Queer European MD passionate about IT
Browse Source

Second assignment.

Davte 4 years ago
parent
commit
4c4ffa048f

+ 3 - 0
.gitignore

@@ -1,6 +1,9 @@
 # ---> TeX
 # ---> TeX
 out/
 out/
 
 
+# SQLite database files
+*.db
+
 ## Core latex/pdflatex auxiliary files:
 ## Core latex/pdflatex auxiliary files:
 *.aux
 *.aux
 *.lof
 *.lof

+ 24 - 5
compitino/main.tex

@@ -4,6 +4,7 @@
 \usepackage{enumitem}
 \usepackage{enumitem}
 \usepackage{forest}
 \usepackage{forest}
 \usepackage{graphicx}
 \usepackage{graphicx}
+\usepackage[hidelinks]{hyperref}
 \usepackage[utf8]{inputenc}
 \usepackage[utf8]{inputenc}
 \usepackage{listings}
 \usepackage{listings}
 \usepackage{pxfonts}
 \usepackage{pxfonts}
@@ -11,6 +12,8 @@
 \usepackage[normalem]{ulem}
 \usepackage[normalem]{ulem}
 \usepackage{geometry}
 \usepackage{geometry}
 
 
+\AfterEndEnvironment{figure}{\noindent}
+
 \geometry{
 \geometry{
 a4paper,
 a4paper,
 top=30mm,
 top=30mm,
@@ -18,7 +21,13 @@ top=30mm,
 
 
 \forestset{qtree/.style={for tree={parent anchor=south,
 \forestset{qtree/.style={for tree={parent anchor=south,
            child anchor=north,align=center,inner sep=0pt}}}
            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}{
 \lstdefinestyle{SQLu}{
     language=SQL,
     language=SQL,
     basicstyle=\small\ttfamily,
     basicstyle=\small\ttfamily,
@@ -34,17 +43,22 @@ top=30mm,
 
 
 
 
 % First page information
 % 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}
 \author{Davide Testa 613565}
-\date{2020-05-15}
+\date{2020-06-01}
 
 
 \begin{document}
 \begin{document}
     \maketitle % Insert the title, author and date
     \maketitle % Insert the title, author and date
+    \input{\folder/source_code.tex}
     \section{Descrizione di massima del dominio (testo)}\label{sec:testo}
     \section{Descrizione di massima del dominio (testo)}\label{sec:testo}
         \input{\folder/testo.tex}
         \input{\folder/testo.tex}
+    \clearpage
     \section{Descrizione del dominio}\label{sec:dominio}
     \section{Descrizione del dominio}\label{sec:dominio}
         \input{\folder/dominio.tex}
         \input{\folder/dominio.tex}
+%    \clearpage
     \section{Schema concettuale}\label{sec:schema-concettuale}
     \section{Schema concettuale}\label{sec:schema-concettuale}
+        La figura~\ref{fig:schema-concettuale} mostra lo schema concettuale in formato
+        grafico.
         \begin{figure}[hb]
         \begin{figure}[hb]
           \centering
           \centering
           \includegraphics[width=\linewidth]{\folder/schema_concettuale.pdf}
           \includegraphics[width=\linewidth]{\folder/schema_concettuale.pdf}
@@ -52,11 +66,14 @@ top=30mm,
           \label{fig:schema-concettuale}
           \label{fig:schema-concettuale}
         \end{figure}
         \end{figure}
         \input{\folder/schema_concettuale.tex}
         \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]
         \begin{figure}[htb]
           \centering
           \centering
           \includegraphics[width=\linewidth]{\folder/schema_logico.pdf}
           \includegraphics[width=\linewidth]{\folder/schema_logico.pdf}
-          \caption{Schema logico in formato grafico}
+          \caption{Schema logico relazionale in formato grafico}
           \label{fig:schema-logico}
           \label{fig:schema-logico}
         \end{figure}
         \end{figure}
         \input{\folder/schema_logico.tex}
         \input{\folder/schema_logico.tex}
@@ -66,4 +83,6 @@ top=30mm,
     \vskip2pc
     \vskip2pc
     \section{Piani di accesso}\label{sec:piani}
     \section{Piani di accesso}\label{sec:piani}
         \input{\folder/piani_di_accesso.tex}
         \input{\folder/piani_di_accesso.tex}
+    \vfill
+    \input{\folder/source_code.tex}
 \end{document}
 \end{document}

+ 72 - 2
compitino/secondo_compitino/dominio.tex

@@ -1,5 +1,75 @@
 % !TEX root = ../main.tex
 % !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.

+ 221 - 21
compitino/secondo_compitino/piani_di_accesso.tex

@@ -3,33 +3,35 @@
 
 
 \paragraph{Piano di accesso logico della query a}
 \paragraph{Piano di accesso logico della query a}
 \begin{center}
 \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{forest}
 \end{center}
 \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}
 \paragraph{Piano di accesso fisico della query a senza indici}
 \begin{center}
 \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}
 \paragraph{Piano di accesso fisico della query a con due indici}
 \begin{center}
 \begin{center}
   \begin{forest}, baseline, qtree
   \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{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{forest}
 \end{center}
 \end{center}
-Indici necessari: \texttt{IndRC} (indice della tabella R sull’attributo C) e \texttt{IndSF} (indice della tabella S sull'attributo F).
+
+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.

+ 116 - 8
compitino/secondo_compitino/queries.tex

@@ -1,14 +1,122 @@
 % !TEX root = ../main.tex
 % !TEX root = ../main.tex
 
 
 \begin{enumerate}[label=\alph*.]
 \begin{enumerate}[label=\alph*.]
-    \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
+    \item Uso di proiezione, join e restrizione.
+
+    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{lstlisting}
 \end{enumerate}
 \end{enumerate}
 \clearpage
 \clearpage

File diff suppressed because it is too large
+ 0 - 0
compitino/secondo_compitino/schema.drawio


BIN
compitino/secondo_compitino/schema_concettuale.pdf


+ 98 - 1
compitino/secondo_compitino/schema_concettuale.tex

@@ -1,3 +1,100 @@
 % !TEX root = ../main.tex
 % !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}

BIN
compitino/secondo_compitino/schema_logico.pdf


+ 49 - 7
compitino/secondo_compitino/schema_logico.tex

@@ -1,21 +1,63 @@
 % !TEX root = ../main.tex
 % !TEX root = ../main.tex
-
+\\
 \textbf{Schema logico relazionale in formato testuale}
 \textbf{Schema logico relazionale in formato testuale}
+
 \begin{lstlisting}[style=SQLu,escapechar=@]
 \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}
 \end{lstlisting}
 
 
 \paragraph{Dipendenze funzionali}
 \paragraph{Dipendenze funzionali}
 \begin{itemize}
 \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}
 \end{itemize}
 
 
-
 Uno schema R, avente insieme di attributi T e insieme di dipendenze funzionali F, (\lstinline{R<T, F>}) è
 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 è
 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.
 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
 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.
 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 - 0
compitino/secondo_compitino/source_code.tex

@@ -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}

File diff suppressed because it is too large
+ 8 - 0
compitino/secondo_compitino/test/data.sql


File diff suppressed because it is too large
+ 108 - 0
compitino/secondo_compitino/test/data_and_schema.sql


+ 192 - 0
compitino/secondo_compitino/test/schema.sql

@@ -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;

+ 86 - 1
compitino/secondo_compitino/testo.tex

@@ -1,3 +1,88 @@
 % !TEX root = ../main.tex
 % !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}

Some files were not shown because too many files changed in this diff