diff --git a/.gitignore b/.gitignore index 7809bf7..86176dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ # ---> TeX +out/ + ## Core latex/pdflatex auxiliary files: *.aux *.lof diff --git a/compitino/main.tex b/compitino/main.tex new file mode 100644 index 0000000..06e1768 --- /dev/null +++ b/compitino/main.tex @@ -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} diff --git a/compitino/primo_compitino/dominio.tex b/compitino/primo_compitino/dominio.tex new file mode 100644 index 0000000..eaf0204 --- /dev/null +++ b/compitino/primo_compitino/dominio.tex @@ -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 l’anno di fondazione. +Per la modalità cartacea interessa anche il comune di stampa, mentre per la modalità +digitalizzata interessa l’URL. +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 all’Albo 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, l’edizione (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, l’identità dell’utente è associata +all’accesso all’articolo 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. \ No newline at end of file diff --git a/compitino/primo_compitino/piani_di_accesso.tex b/compitino/primo_compitino/piani_di_accesso.tex new file mode 100644 index 0000000..6008854 --- /dev/null +++ b/compitino/primo_compitino/piani_di_accesso.tex @@ -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 sull’attributo 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} +L’output dell’operatore $\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$, l’ordinamento 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 sull’attributo IdUtente). + +Non occorre ordinare prima di raggruppare: l’output di IndexFilter è già ordinato sull’insieme di attributi dell’indice (in questo caso \texttt{\{v.IdUtente\}}), che coincide nel nostro caso con l’insieme 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} +L’output 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 sull’attributo IdArticolo), IndVIdA (indice +della tabella Visite sull’attributo IdArticolo). +Non occorre fare un ordinamento sull’output della IndexNestedLoop: avendo ricevuto input +ordinato su IdArticolo dalla IndexFilter, e sapendo che \texttt{a.IdArticolo $\to$ a.Titolo}, l’input +della groupby è già raggruppato per le dimensioni di analisi. diff --git a/compitino/primo_compitino/queries.tex b/compitino/primo_compitino/queries.tex new file mode 100644 index 0000000..2138410 --- /dev/null +++ b/compitino/primo_compitino/queries.tex @@ -0,0 +1,119 @@ +% !TEX root = ../main.tex + +\begin{enumerate}[label=\alph*.] + \item Uso di proiezione, join e restrizione + + Riportare il titolo e l’URL 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 un’ora o più (probabilmente l’utente avrà lasciato aperta la + pagina, non stava davvero leggendo: sui nostri siti peraltro dopo 55 minuti di inattività + si attiva una animazione che copre l’articolo e chiede di premere un pulsante per + continuare a leggere, altrimenti esegue il logout dell’utente dopo 5 minuti) e ordinando + dall’utente 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 all’albo 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} \ No newline at end of file diff --git a/compitino/primo_compitino/schema_concettuale.pdf b/compitino/primo_compitino/schema_concettuale.pdf new file mode 100644 index 0000000..9ddd060 Binary files /dev/null and b/compitino/primo_compitino/schema_concettuale.pdf differ diff --git a/compitino/primo_compitino/schema_concettuale.tex b/compitino/primo_compitino/schema_concettuale.tex new file mode 100644 index 0000000..face92e --- /dev/null +++ b/compitino/primo_compitino/schema_concettuale.tex @@ -0,0 +1,4 @@ +% !TEX root = ../main.tex + +Vincolo non catturato graficamente: un articolo può avere visite solo se la sua edizione è +online. \ No newline at end of file diff --git a/compitino/primo_compitino/schema_logico.pdf b/compitino/primo_compitino/schema_logico.pdf new file mode 100644 index 0000000..8a0b33d Binary files /dev/null and b/compitino/primo_compitino/schema_logico.pdf differ diff --git a/compitino/primo_compitino/schema_logico.tex b/compitino/primo_compitino/schema_logico.tex new file mode 100644 index 0000000..755284c --- /dev/null +++ b/compitino/primo_compitino/schema_logico.tex @@ -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 (l’Ordine dei Giornalisti si cura di non attribuire lo stesso numero di iscrizione all’albo 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}) è +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 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. diff --git a/compitino/primo_compitino/testo.tex b/compitino/primo_compitino/testo.tex new file mode 100644 index 0000000..b19633f --- /dev/null +++ b/compitino/primo_compitino/testo.tex @@ -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à. \ No newline at end of file