123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- % !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.
|