252 lines
10 KiB
TeX
252 lines
10 KiB
TeX
% !TEX root = ../main.tex
|
||
\subsection{Query a}
|
||
|
||
\paragraph{Piano di accesso logico della query a}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{$\pi^{b}$ r.IdRicetta , r.Nome}
|
||
[{$\bowtie$ p.IdPersona = r.IdCreatrice}
|
||
[{$\sigma$ p.Nome = 'Giovanni'}
|
||
[Persone p]
|
||
]
|
||
[Ricette r]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
|
||
Non c'è in questo caso differenza tra $\pi^{b}$ e $\pi$: non ci possono essere
|
||
duplicati.
|
||
\paragraph{Piano di accesso fisico della query a senza indici}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{Project(\{r.IdRicetta , r.Nome\})}
|
||
[{SortMerge(p.IdPersona = r.IdCreatrice)}
|
||
[{Sort([p.IdPersona])}
|
||
[{Project(\{p.IdPersona\})}
|
||
[{Filter(p.Nome = 'Giovanni')}
|
||
[{TableScan(Persone p)}]
|
||
]
|
||
]
|
||
]
|
||
[{Sort([r.IdCreatrice])}
|
||
[{Project(\{r.IdRicetta, r.Nome, r.IdCreatrice\})}
|
||
[{TableScan(Ricette r)}]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
|
||
\paragraph{Piano di accesso fisico della query a con due indici}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{Project(\{r.IdRicetta , r.Nome\})}
|
||
[{IndexNestedLoop(p.IdPersona = r.IdCreatrice)}
|
||
[{IndexFilter(Persone p,\\ IndPN, p.Nome = 'Giovanni')}]
|
||
[{IndexFilter(Ricette r,\\IndRC, r.IdCreatrice = p.IdPersona)}]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
Indici necessari: \texttt{IndPN} (indice della tabella Persone sull’attributo
|
||
Nome) e \texttt{IndRC} (indice della tabella Ricette sull'attributo Creatrice).
|
||
|
||
\clearpage
|
||
\subsection{Query b}
|
||
|
||
\paragraph{Piano di accesso logico della query b}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{$\tau$[-DiversiFornitori]}
|
||
[{$\pi^{b}$ fa.IdBirrificio, COUNT(DISTINCT fa.IdFornitore) DiversiFornitori}
|
||
[{$\sigma$ COUNT(DISTINCT fa.IdFornitore) $>=$ 3}
|
||
[\{fa.IdBirrificio\} {$\gamma$ \{COUNT(DISTINCT fa.IdFornitore)\}}
|
||
[{$\sigma$ fa.Data $>=$ '2020-01-01'}
|
||
[Fatture fa]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
|
||
Non c'è in questo caso differenza tra $\pi^{b}$ e $\pi$: non ci possono essere
|
||
duplicati, in quanto la GROUP BY raggruppa per IdBirrificio.
|
||
\paragraph{Piano di accesso fisico della query b senza indici}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{Sort[-DiversiFornitori]}
|
||
[{Project(\{fa.IdBirrificio, COUNT(DISTINCT fa.IdFornitore) DiversiFornitori\})}
|
||
[{Filter(COUNT(DISTINCT fa.IdFornitore) $>=$ 3)}
|
||
[{GroupBy(\{fa.IdBirrificio\}, \{COUNT(DISTINCT fa.IdFornitore)\})}
|
||
[{Sort([fa.IdBirrificio])}
|
||
[{Filter(fa.Data $>=$ '2020-01-01')}
|
||
[{TableScan(Fatture fa)}]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
Il sort sull'attributo dimensione di analisi prima della GroupBy è necessario,
|
||
in quanto non è garantito che i record della tabella Fatture siano raggruppati
|
||
per IdBirrificio.
|
||
Lo sarebbero se l'organizzazione primaria della tabella fosse sequenziale
|
||
proprio su questo attributo, il che è estremamente poco probabile.
|
||
\clearpage
|
||
\paragraph{Piano di accesso fisico della query b con un indice}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{Sort[-DiversiFornitori]}
|
||
[{Project(\{fa.IdBirrificio, COUNT(DISTINCT fa.IdFornitore) DiversiFornitori\})}
|
||
[{Filter(COUNT(DISTINCT fa.IdFornitore) $>=$ 3)}
|
||
[{GroupBy(\{fa.IdBirrificio\}, \{COUNT(DISTINCT fa.IdFornitore)\})}
|
||
[{Sort([fa.IdBirrificio])}
|
||
[{IndexFilter(Fatture fa, IndFD, fa.Data $>=$ '2020-01-01')}]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
Indice necessario: \texttt{IndFD} (indice della tabella Fatture sull’attributo
|
||
Data).
|
||
Il sort sull'attributo IdBirrificio prima della GroupBy è necessario, in quanto
|
||
i record in input sono ordinati per data, il che non ci garantisce che siano
|
||
raggruppati per IdBirrificio (che è dimensione di analisi).
|
||
|
||
\subsection{Query c}
|
||
|
||
\paragraph{Piano di accesso logico della query c}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{$\pi^{b}$ fo.RagioneSociale, SUM(fa.Importo) ImportoTotale, AVG(fa.Importo) ImportoMedio}
|
||
[$\sigma$ SUM(fa.Importo) $>$ 10
|
||
[{\{fo.IdFornitore, fo.RagioneSociale\} $\gamma$ \{SUM(fa.Importo), AVG(fa.Importo)\}}
|
||
[{$\bowtie$ fa.IdFornitore = fo.IdFornitore}
|
||
[{Fornitori fo}]
|
||
[{$\bowtie$ fa.IdBirrificio = b.IdBirrificio}
|
||
[{$\sigma$ b.Nome = 'Pirati Rossi'}
|
||
[{Birrifici b}]
|
||
]
|
||
[{Fatture fa}]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
|
||
In questo caso non ci dovrebbe essere differenza tra $\pi^{b}$ e $\pi$: non ci
|
||
devono essere due fornitori con la stessa ragione sociale (la ragione sociale
|
||
è chiave naturale); è comunque possibile un errore di inserimento se non ho
|
||
impostato un vincolo di unicità anche su questo attributo, che non ho scelto
|
||
come chiave primaria della tabella: ecco perché ho raggruppato anche per
|
||
IdFornitore e non solo per RagioneSociale.
|
||
|
||
Ho scelto l'ordine di giunzione in modo da avere la restrizione il più distale
|
||
possibile.
|
||
|
||
\clearpage
|
||
|
||
\paragraph{Piano di accesso fisico della query c senza indici}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{Project(\{fo.RagioneSociale,\\SUM(fa.Importo) ImportoTotale, AVG(fa.Importo) ImportoMedio\})}
|
||
[{Filter(SUM(fa.Importo) $>$ 10)}
|
||
[{GroupBy(\{fo.IdFornitore,fo.RagioneSociale\}, \{SUM(fa.Importo), AVG(fa.Importo)\})}
|
||
[{MergeSort(fa.IdFornitore = fo.IdFornitore)}
|
||
[{Sort([fo.IdFornitore])}
|
||
[{Project(\{fo.IdFornitore,\\fo.RagioneSociale\})}
|
||
[{Fornitori fo}]
|
||
]
|
||
]
|
||
[{Sort([fa.IdFornitore])}
|
||
[{Project(\{fa.IdFornitore, fa.Importo\})}
|
||
[{MergeSort(fa.IdBirrificio = b.IdBirrificio)}
|
||
[{Sort([b.IdBirrificio])}
|
||
[{Project(\{b.IdBirrificio\})}
|
||
[{Filter(b.Nome = 'Pirati Rossi')}
|
||
[{TableScan(Birrifici b)}]
|
||
]
|
||
]
|
||
]
|
||
[{Sort([fa.IdBirrificio])}
|
||
[{Project(\{fa.IdBirrificio,\\fa.IdFornitore fa.Importo\})}
|
||
[{Fatture fa}]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
Non è necessario ordinare per \texttt{[fo.IdFornitore, fo.RagioneSociale]} prima
|
||
della GroupBy: per costruzione, l'ordine dell'operatore esterno della SortMerge
|
||
viene mantenuto nell'output, e questo ordine è sull'attributo fo.IdFornitore,
|
||
che a sua volta determina funzionalmente l'altra dimensione di analisi, fo.RagioneSociale.
|
||
|
||
Pertanto, è garantito che l'input della GroupBy sarà già raggruppato per gli
|
||
attributi che sono dimensione di analisi e non occorre un ordinamento
|
||
preventivo.
|
||
|
||
\clearpage
|
||
\paragraph{Piano di accesso fisico della query c con tre indici}
|
||
\begin{center}
|
||
\begin{forest}, baseline, qtree
|
||
[{Project(\{fo.RagioneSociale,\\SUM(fa.Importo) ImportoTotale, AVG(fa.Importo) ImportoMedio\})}
|
||
[{Filter(SUM(fa.Importo) $>$ 10)}
|
||
[{GroupBy(\{fo.IdFornitore, fo.RagioneSociale\},\\\{SUM(fa.Importo), AVG(fa.Importo)\})}
|
||
[{Sorted([fo.IdFornitore])}
|
||
[{Project(\{fo.IdFornitore, fo.RagioneSociale, fa.Importo\})}
|
||
[{IndexNestedLoop(fa.IdFornitore = fo.IdFornitore)}
|
||
[{IndexNestedLoop\\(fa.IdBirrificio = b.IdBirrificio)}
|
||
[{IndexFilter(Birrifici b, IndBN,\\b.Nome = 'Pirati Rossi')}]
|
||
[{IndexFilter(Fatture fa, IndFaIdB,\\fa.IdBirrificio = b.IdBirrificio)}]
|
||
]
|
||
[{IndexFilter(Fornitori fo, IndFoIdF,\\fo.IdFornitore = fa.IdFornitore)}]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
]
|
||
\end{forest}
|
||
\end{center}
|
||
Indici necessari: \texttt{IndBN} (indice della tabella Birrifici sull’attributo
|
||
Nome), \texttt{IndFaIdB} (indice della tabella Fatture sull'attributo
|
||
IdBirrificio) e \texttt{IndFoIdF} (indice della tabella Fornitori sull'attributo
|
||
IdFornitore).
|
||
|
||
Occorre ordinare per IdFornitore prima della \texttt{GroupBy}, in quanto
|
||
l'output della IndexNestedLoop è ordinato come l'operatore esterno, ovvero
|
||
per nome del birrificio.
|
||
|
||
Potrei spostare l'ordinamento tra le due giunzioni con IndexNestedLoop, tanto
|
||
ogni fattura ha un fornitore e l'output non andrà a decrecere dopo la seconda
|
||
giunzione (anzi, si arricchirà di campi).
|
||
Il sort andrebbe fatto con il minor numero possibile di dati, dato l'alto costo
|
||
dell'algoritmo, eliminando i campi superflui con una project prima.
|
||
|
||
Il vantaggio dell'IndexNestedLoop sul SortMerge si ha solo se la condizione è
|
||
sufficientemente restrittiva da essere soddisfatta da una piccola minoranza
|
||
di record.
|
||
In questo caso, la restrizione sul nome del birrificio dovrebbe essere
|
||
abbastanza restrittiva (se ci sono abbastanza birrifici, il numero di
|
||
birrifici con il nome `Pirati Rossi' sarà trascurabile rispetto al totale) ed
|
||
è ragionevole che le fatture che riguardano quel birrificio siano una esigua
|
||
minoranza rispetto al totale delle fatture.
|
||
Se così non fosse, pur avendo i tre indici a disposizione, converrebbe
|
||
utilizzare comunque il SortMerge.
|