% !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}