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