119 lines
5.2 KiB
TeX
119 lines
5.2 KiB
TeX
% !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} |