Queer European MD passionate about IT
Răsfoiți Sursa

First assignment uploaded

Davte 4 ani în urmă
părinte
comite
6531fb6d49

+ 2 - 0
.gitignore

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

+ 69 - 0
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}

+ 22 - 0
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.

+ 191 - 0
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.

+ 119 - 0
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}

BIN
compitino/primo_compitino/schema_concettuale.pdf


+ 4 - 0
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.

BIN
compitino/primo_compitino/schema_logico.pdf


+ 38 - 0
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<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 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.

+ 9 - 0
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à.