First assignment uploaded

This commit is contained in:
Davte 2020-05-20 12:33:44 +02:00
parent 9c5714851a
commit 6531fb6d49
10 changed files with 454 additions and 0 deletions

2
.gitignore vendored
View File

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

69
compitino/main.tex Normal file
View File

@ -0,0 +1,69 @@
\documentclass [11pt, a4paper]{article}
\usepackage[main= italian, english]{babel}
\usepackage{enumitem}
\usepackage{forest}
\usepackage{graphicx}
\usepackage[utf8]{inputenc}
\usepackage{listings}
\usepackage{pxfonts}
\usepackage{textcomp}
\usepackage[normalem]{ulem}
\usepackage{geometry}
\geometry{
a4paper,
top=30mm,
}
\forestset{qtree/.style={for tree={parent anchor=south,
child anchor=north,align=center,inner sep=0pt}}}
\lstset{upquote=true,showstringspaces=false}
\lstdefinestyle{SQLu}{
language=SQL,
basicstyle=\small\ttfamily,
keywordstyle=\bfseries,
moredelim=[is][\underbar]{_}{_},
keepspaces=true
}
\selectlanguage{italian}
\setcounter{section}{-1}
\newcommand{\folder}{primo_compitino}
%\newcommand{\folder}{secondo_compitino}
% First page information
\title{\textbf{Basi di dati prof. Ghelli}\linebreak\textit{``Sempre sul pezzo"}}
\author{Davide Testa 613565}
\date{2020-05-15}
\begin{document}
\maketitle % Insert the title, author and date
\section{Descrizione di massima del dominio (testo)}\label{sec:testo}
\input{\folder/testo.tex}
\section{Descrizione del dominio}\label{sec:dominio}
\input{\folder/dominio.tex}
\section{Schema concettuale}\label{sec:schema-concettuale}
\begin{figure}[hb]
\centering
\includegraphics[width=\linewidth]{\folder/schema_concettuale.pdf}
\caption{Schema concettuale in formato grafico}
\label{fig:schema-concettuale}
\end{figure}
\input{\folder/schema_concettuale.tex}
\section{Schema logico}\label{sec:schema-logico}
\begin{figure}[htb]
\centering
\includegraphics[width=\linewidth]{\folder/schema_logico.pdf}
\caption{Schema logico in formato grafico}
\label{fig:schema-logico}
\end{figure}
\input{\folder/schema_logico.tex}
\clearpage
\section{Interrogazioni}\label{sec:queries}
\input{\folder/queries.tex}
\vskip2pc
\section{Piani di accesso}\label{sec:piani}
\input{\folder/piani_di_accesso.tex}
\end{document}

View File

@ -0,0 +1,22 @@
% !TEX root = ../main.tex
La catena editoriale ``Sempre sul pezzo" pubblica giornali cartacei e gestisce siti di notizie. Per
ciascuna modalità editoriale, interessa conoscere il titolo, il direttore e lanno di fondazione.
Per la modalità cartacea interessa anche il comune di stampa, mentre per la modalità
digitalizzata interessa lURL.
Per ogni modalità editoriale lavorano dei giornalisti, che possono essere dipendenti oppure
free-lance e possono pubblicare articoli per più modalità editoriali. Di tutti i giornalisti
interessa conoscere il numero di iscrizione allAlbo dei Giornalisti (se presente), il cognome, il
nome e tenere traccia degli articoli pubblicati (ogni articolo può avere uno o più autori). Dei
dipendenti interessa il codice INPS per versare i tributi, mentre dei free-lance la partita IVA per
emettere fatture.
Degli articoli interessa il titolo, il sottotitolo, ledizione (cartacea o digitale) in cui sono
pubblicati (ogni articolo è pubblicato in una sola edizione) e la data di pubblicazione. Ci sono
articoli visibili a tutti e articoli premium riservati agli abbonati.
I siti Web sono dotati di un sistema di tracciamento che registra, per ogni articolo, il numero di
visite e il tempo dedicato alla lettura per ogni visita.
Gli utenti possono avere un abbonamento: in questo caso, lidentità dellutente è associata
allaccesso allarticolo online. Degli utenti interessa nome, cognome e indirizzo.
Degli abbonamenti, si vuole tenere traccia della data di sottoscrizione e quella di scadenza.
Ogni abbonamento è associato a un sito web di notizie, ogni utente può avere più
abbonamenti.

View File

@ -0,0 +1,191 @@
% !TEX root = ../main.tex
\subsection{Query a}
\paragraph{Piano di accesso logico della query a}
\begin{center}
\begin{forest}
[{$\pi^{b}$ e.Titolo, sw.Url}
[{$\bowtie$ e.IdEdizione = sw.IdEdizione}
[{$\sigma$e.Titolo $>=$'G' $\wedge$ e.Titolo $<$ 'H'}
[Edizioni e]
]
[SitiWeb sw]
]
]
\end{forest}
\end{center}
\paragraph{Piano di accesso fisico della query a senza indici}
\begin{center}
\begin{forest}
[{Project(\{e.Titolo, sw.Url\})}
[{SortMerge(e.IdEdizione = sw.IdEdizione)}
[{Sort(\{e.IdEdizione\})}
[{Project(\{e.Titolo, e.IdEdizione\})}
[{Filter(e.Titolo $>=$ 'G' AND e.Titolo $<$ H)}
[{TableScan(Edizioni e)}]
]
]
]
[{Sort(\{sw.IdEdizione\})}
[{Project(\{sw.Url, sw.IdEdizione\})}
[{TableScan(SitiWeb sw)}]
]
]
]
]
\end{forest}
\end{center}
\paragraph{Piano di accesso fisico della query a con due indici}
\begin{center}
\begin{forest}, baseline, qtree
[{Project(\{e.Titolo, sw.Url\})}
[{IndexNestedLoop(e.IdEdizione = sw.IdEdizione)}
[{IndexFilter(Edizioni e, IndETit,\\ e.Titolo $>=$ 'G' AND e.Titolo $<$ H)}]
[{IndexFilter(SitiWeb sw, IndSWIdEd,\\ e.IdEdizione = sw.IdEdizione)}]
]
]
\end{forest}
\end{center}
Indici necessari: \texttt{IndETit} (indice della tabella Edizioni sullattributo Titolo) e \texttt{IndSWIdEd} (indice della tabella SitiWeb sulla chiave IdEdizione).
\subsection{Query b}
\paragraph{Piano di accesso logico della query b}
\begin{center}
\begin{forest}
[{$\tau$(\{$-$TempoTotaleLettura\})}
[{$\pi^{b}$ v.IdUtente, SUM(v.TempoLettura) TempoTotaleLettura}
[{$\sigma$ SUM(v.TempoLettura) $>$ 10}
[{\{v.IdUtente\} $\gamma$ \{SUM(v.TempoLettura)\}}
[{$\sigma$ v.TempoLettura $<$ 60}
[{Visite v}]
]
]
]
]
]
\end{forest}
\end{center}
Loutput delloperatore $\gamma$ è già proiettato sulle sole dimensioni di analisi e risultati delle
funzioni di aggregazione, non occorrerebbe proiettare su \texttt{\{v.IdUtente, SUM(v.TempoLettura)\}}; lo faccio per poter rinominare in TempoTotaleLettura il risultato della SUM.
Nella $\tau$, lordinamento discendente è reso dal segno `$-$' per convenzione (in questo caso comunque il tipo è numerico).
\paragraph{Piano di accesso fisico della query b senza indici}
\begin{center}
\begin{forest}
[{Sort(\{$-$TempoTotaleLettura\})}
[{Project(\{v.IdUtente, SUM(v.TempoLettura) TempoTotaleLettura\})}
[{Filter(SUM(v.TempoLettura) $>$ 10)}
[{GroupBy(\{v.IdUtente\}, \{SUM(v.TempoLettura)\})}
[{Sort(\{v.IdUtente\})}
[{Project(\{v.IdUtente, v.TempoLettura\})}
[{Filter(v.TempoLettura $<$ 60)}
[{TableScan(Visite v)}]
]
]
]
]
]
]
]
\end{forest}
\end{center}
\clearpage
\paragraph{Piano di accesso fisico della query b con indici}
\begin{center}
\begin{forest}, baseline, qtree
[{Sort(\{$-$TempoTotaleLettura\})}
[{Project(\{v.IdUtente, SUM(v.TempoLettura) TempoTotaleLettura\})}
[{GroupBy(\{v.IdUtente\}, \{SUM(v.TempoLettura)\})}
[{\sout{Project(\{v.IdUtente, v.TempoLettura\})}}
[{IndexFilter(Visite v, IndVIdUt, v.TempoLettura $<$ 60)}
]
]
]
]
]
]
]
\end{forest}
\end{center}
Indice necessario: \texttt{IndVIdUt} (indice della tabella Visite sullattributo IdUtente).
Non occorre ordinare prima di raggruppare: loutput di IndexFilter è già ordinato sullinsieme di attributi dellindice (in questo caso \texttt{\{v.IdUtente\}}), che coincide nel nostro caso con linsieme delle dimensioni di analisi.
Non occorre nemmeno proiettare su \texttt{\{v.IdUtente, v.TempoLettura\}} prima del raggruppamento, in quanto la groupby può ricevere il record direttamente dalla IndexFilter e scartare gli attributi non rilevanti (cioè quelli che non sono dimensione di analisi né input delle funzioni di aggregazione).
\vfill
\subsection{Query c}
\paragraph{Piano di accesso logico della query c}
\begin{center}
\begin{forest}, baseline, qtree
[{$\pi$ a.IdArticolo, a.Titolo, COUNT(*) NumeroVisite,\\SUM(v.TempoLettura) TempoTotaleLettura}
[{$\sigma$ COUNT(*) $>=$ 3}
[{\{a.IdArticolo, a.Titolo\} $\gamma$ \{COUNT(*), SUM(v.TempoLettura)\}}
[{$\bowtie$ a.IdArticolo = v.IdArticolo}
[{$\sigma$ a.Premium = 'N'}
[{Articoli a}]
]
[{Visite v}]
]
]
]
]
\end{forest}
\end{center}
\clearpage
\paragraph{Piano di accesso fisico della query c senza indici}
\begin{center}
\begin{forest}, baseline, qtree
[{Project(\{a.IdArticolo, a.Titolo, COUNT(*) NumeroVisite,\\SUM(v.TempoLettura) TempoTotaleLettura\})}
[{Filter(COUNT(*) $>=$ 3)}
[{GroupBy(\{a.IdArticolo, a.Titolo\}, \{COUNT(*), SUM(v.TempoLettura)\})}
[{SortMerge(a.IdArticolo = v.IdArticolo)}
[{Sort(a.IdArticolo)}
[{Project(\{a.IdArticolo, a.Titolo\})}
[{Filter(a.Premium = 'N')}
[{TableScan(Articoli a)}]
]
]
]
[{Sort(v.IdArticolo)}
[{Project(\{v.IdArticolo, v.TempoLettura\})}
[{TableScan(Visite v)}]
]
]
]
]
]
]
\end{forest}
\end{center}
Loutput di SortMerge è già ordinato per a.IdArticolo e, dato che \texttt{a.IdArticolo $\to$ a.Titolo},
non occorre ordinare prima della groupby.
\paragraph{Piano di accesso fisico della query c con due indici}
\begin{center}
\begin{forest}, baseline, qtree
[{Project(\{a.IdArticolo, a.Titolo, COUNT(*) NumeroVisite,\\SUM(v.TempoLettura) TempoTotaleLettura\})}
[{Filter(COUNT(*) $>=$ 3)}
[{GroupBy(\{a.IdArticolo, a.Titolo\}, \{COUNT(*), SUM(v.TempoLettura)\})}
[{IndexNestedLoop(a.IdArticolo = v.IdArticolo)}
[{Project(\{a.IdArticolo, a.Titolo\})}
[{IndexFilter(Articoli a,\\ IndAIdA, a.Premium = 'N')}
]
]
[{Project(\{v.IdArticolo, v.TempoLettura\})}
[{IndexFilter(Visite v,\\IndVIdA, v.IdArticolo = a.IdArticolo)}]
]
]
]
]
]
\end{forest}
\end{center}
Indici necessari: IndAIdA (indice della tabella Articoli sullattributo IdArticolo), IndVIdA (indice
della tabella Visite sullattributo IdArticolo).
Non occorre fare un ordinamento sulloutput della IndexNestedLoop: avendo ricevuto input
ordinato su IdArticolo dalla IndexFilter, e sapendo che \texttt{a.IdArticolo $\to$ a.Titolo}, linput
della groupby è già raggruppato per le dimensioni di analisi.

View File

@ -0,0 +1,119 @@
% !TEX root = ../main.tex
\begin{enumerate}[label=\alph*.]
\item Uso di proiezione, join e restrizione
Riportare il titolo e lURL dei siti web il cui titolo inizia con la lettera G (compreso il caso
in cui il titolo sia esattamente 'G).
\begin{lstlisting}[style=SQLu]
SELECT e.Titolo, sw.Url
FROM Edizioni e
JOIN SitiWeb sw ON e.IdEdizione = sw.IdEdizione
WHERE e.Titolo >= 'G' AND e.Titolo < 'H'
\end{lstlisting}
\item Uso di group by con having, where e sort
Riportare IdUtente e tempo totale di lettura, purché superiore a 10 minuti, escludendo
le sessioni di lettura lunghe unora o più (probabilmente lutente avrà lasciato aperta la
pagina, non stava davvero leggendo: sui nostri siti peraltro dopo 55 minuti di inattività
si attiva una animazione che copre larticolo e chiede di premere un pulsante per
continuare a leggere, altrimenti esegue il logout dellutente dopo 5 minuti) e ordinando
dallutente che ha passato più minuti a leggere a quello che ne ha passati meno.
\begin{lstlisting}[style=SQLu]
SELECT v.IdUtente, SUM(v.TempoLettura) TempoTotaleLettura
FROM Visite v
WHERE v.TempoLettura < 60
GROUP BY v.IdUtente
HAVING SUM(v.TempoLettura) > 10
ORDER BY SUM(v.TempoLettura) DESC
\end{lstlisting}
\item Uso di join, group by con having e where
Riportare il codice articolo, il titolo, il numero di visite e il tempo totale di lettura degli
articoli ad accesso libero (ovvero non premium) visti almeno 3 volte.
\begin{lstlisting}[style=SQLu]
SELECT a.IdArticolo, a.Titolo, COUNT(*) NumeroVisite,
SUM(v.TempoLettura) TempoTotaleLettura
FROM Visite v
JOIN Articoli a ON a.IdArticolo = v.IdArticolo
WHERE a.Premium = 'N'
GROUP BY a.IdArticolo, a.Titolo
HAVING COUNT(*) >= 3
\end{lstlisting}
Posso fare COUNT(*) perché la giunzione con chiave esterna IdArticolo (che è chiave
primaria della tabella Articoli) avrà tante righe quante ce ne sono nella tabella Visite:
per ogni riga di Visite cè una sola riga di Articoli.
Raggruppo anche per titolo oltre che per IdArticolo (anche se IdArticolo → Titolo)
perché proietto poi anche il Titolo.
\item Uso di select annidata con quantificazione esistenziale
Riportare Cognome e Nome dei giornalisti che hanno scritto almeno un articolo sul
giornale “Il Titanio".
\begin{lstlisting}[style=SQLu]
SELECT g.Cognome, g.Nome
FROM Giornalisti g
WHERE EXISTS (SELECT *
FROM ArticoliGiornalisti ag
JOIN Articoli a ON a.IdArticolo = ag.IdArticolo
JOIN Edizioni e ON e.IdEdizione = a.IdEdizione
WHERE ag.IdGiornalista = g.IdGiornalista
AND e.Titolo = 'Il Titanio')
\end{lstlisting}
NOTA: avrei potuto fare una giunzione su IdGiornalista anziché una quantificazione
esistenziale.
\item Uso di select annidata con quantificazione universale
Riportare Cognome e Nome dei giornalisti che hanno scritto solo articoli per giornali
diretti da Peppone.
Esprimo in notazione insiemistica:
\begin{lstlisting}[style=SQLu,escapechar=@]
{g.Cognome, g.Nome | g @$\in$@ Giornalisti .
@$\forall$@ (ag @$\in$@ ArticoliGiornalisti, a @$\in$@ Articoli, e @$\in$@ Edizioni,
a.IdArticolo = ag.IdArticolo,
e.IdEdizione = a.IdEdizione,
ag.IdGiornalista = g.IdGiornalista) .
(e.Direttore = 'Peppone')}
\end{lstlisting}
Trasformando il $\forall x.P$ in $\neg\exists x.\neg P$:
\begin{lstlisting}[style=SQLu,escapechar=@]
{g.Cognome, g.Nome | g @$\in$@ Giornalisti .
@$\neg\exists$@ (ag @$\in$@ ArticoliGiornalisti, a @$\in$@ Articoli, e @$\in$@ Edizioni,
a.IdArticolo = ag.IdArticolo,
e.IdEdizione = a.IdEdizione,
ag.IdGiornalista = g.IdGiornalista) .
(e.Direttore = 'Peppone')}
\end{lstlisting}
Scrivo quindi la query:
\begin{lstlisting}[style=SQLu,escapechar=@]
SELECT g.Cognome, g.Nome
FROM Giornalisti g
WHERE NOT EXISTS (SELECT *
FROM ArticoliGiornalisti ag
JOIN Articoli a
ON a.IdArticolo = ag.IdArticolo
JOIN Edizioni e
ON e.IdEdizione = a.IdEdizione
WHERE ag.IdGiornalista = g.IdGiornalista
AND e.Direttore != 'Peppone')
\end{lstlisting}
NOTA: i giornalisti che non hanno scritto articoli compariranno nel risultato.
\item Uso di subquery di confronto quantificato usando una subquery di tipo scalare
Voglio premiare il dipendente che esercita la professione da più tempo: per fare questo,
ho bisogno di sapere il codice INPS del giornalista con numero di iscrizione allalbo più
basso.
\begin{lstlisting}[style=SQLu,escapechar=@]
SELECT gd.CodiceInps
FROM GiornalistiDipendenti gd
JOIN Giornalisti g ON g.IdGiornalista = gd.IdGiornalista
WHERE g.NumeroAlbo = ANY(SELECT MIN(g2.NumeroALbo)
FROM Giornalisti g2
JOIN GiornalistiDipendenti gd2
ON g2.IdGiornalista = gd2.IdGiornalista)
\end{lstlisting}
La subquery scalare restituisce un singolo valore, mentre il confronto quantificato è utile
quando la subquery restituisce un insieme di più valori. \lstinline[language=SQL]{= ANY} equivale a \lstinline[language=SQL]{IN}.
\end{enumerate}

Binary file not shown.

View File

@ -0,0 +1,4 @@
% !TEX root = ../main.tex
Vincolo non catturato graficamente: un articolo può avere visite solo se la sua edizione è
online.

Binary file not shown.

View File

@ -0,0 +1,38 @@
% !TEX root = ../main.tex
\textbf{Schema logico relazionale in formato testuale}
\begin{lstlisting}[style=SQLu,escapechar=@]
Giornali(@\underline{IdEdizione*}@, ComuneStampa)
SitiWeb(@\underline{IdEdizione*}@, Url)
Edizioni(@\underline{IdEdizione}@, Titolo, Direttore, AnnoFondazione)
Articoli(@\underline{IdArticolo}@, IdEdizione*, Titolo, Sottotitolo,
DataPubblicazione, Premium)
Visite(@\underline{IdVisita}@, IdArticolo*, TempoLettura, IdUtente*)
Utenti(@\underline{IdUtente}@, Nome, Cognome, Indirizzo)
Abbonamenti(@\underline{IdUtente*, IdEdizione*, DataSottoscrizione}@, DataScadenza)
ArticoliGiornalisti(@\underline{IdArticolo*, IdGiornalista*}@)
Giornalisti(@\underline{IdGiornalista}@, NumeroAlbo, Cognome, Nome)
GiornalistiDipendenti(@\underline{IdGiornalista*}@, CodiceInps)
GiornalistiFreeLance(@\underline{IdGiornalista*}@, PartitaIva)
\end{lstlisting}
\paragraph{Dipendenze funzionali}
\begin{itemize}
\item Per ogni tabella la chiave primaria (sottolineata) determina ciascuno degli attributi della tabella ($\{IdEdizione \to Titolo, IdEdizione \to Direttore, \textellipsis\}$)
\item Nella tabella Giornalisti, vale inoltre che $\{NumeroAlbo \to Nome;$ $NumeroAlbo \to Cognome;$ $NumeroAlbo \to IdGiornalista\}$: NumeroAlbo è una chiave naturale (lOrdine dei Giornalisti si cura di non attribuire lo stesso numero di iscrizione allalbo a due giornalisti diversi), ho scelto di aggiungere la chiave artificiale IdGiornalista prevedendo possibili errori di inserimento da parte della segreteria della catena editoriale: in queto modo, il NumeroAlbo può essere modificato senza problemi in caso di errori. Discorso analogo per CodiceInps e PartitaIva.
\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 lintera 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 i determinanti sono tutti 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}.
NOTA: assumo che la stringa “Indirizzo” e simili siano attributi atomici, anche se forse nella
realtà sarebbero meglio rappresentati in altro modo.

View File

@ -0,0 +1,9 @@
% !TEX root = ../main.tex
Una catena di quotidiani vuole gestire informazioni relative ad articoli scritti da giornalisti che
possono essere dipendenti oppure free-lance, e i quali possono essere pubblicati sia su un
solo quotidiano che su diversi quotidiani del gruppo. La catena stampa versioni cartacee ma
pubblica anche siti web. I siti web sono muniti di sistemi di tracciamento che permettono di
conoscere il numero di click su ogni articolo e il tempo dedicato alla lettura. La catena è anche
interessata a gestire gli abbonamenti. Quando un abbonato opera sul sito web, la sua identità
è nota, e quindi i suoi click sono associati alla sua identità.