|
@@ -0,0 +1,120 @@
|
|
|
+% !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}
|
|
|
+%\end{enumerate}
|