% !TEX root = ../main.tex \subsection{Query a} \paragraph{Piano di accesso logico della query a} \begin{center} \begin{forest} [{$\pi^{b}$ e.Titolo, sw.Url} [{$\bowtie$ e.IdEdizione = sw.IdEdizione} [{$\sigma$e.Titolo $>=$'G' $\wedge$ e.Titolo $<$ 'H'} [Edizioni e] ] [SitiWeb sw] ] ] \end{forest} \end{center} \paragraph{Piano di accesso fisico della query a senza indici} \begin{center} \begin{forest} [{Project(\{e.Titolo, sw.Url\})} [{SortMerge(e.IdEdizione = sw.IdEdizione)} [{Sort(\{e.IdEdizione\})} [{Project(\{e.Titolo, e.IdEdizione\})} [{Filter(e.Titolo $>=$ 'G' AND e.Titolo $<$ H)} [{TableScan(Edizioni e)}] ] ] ] [{Sort(\{sw.IdEdizione\})} [{Project(\{sw.Url, sw.IdEdizione\})} [{TableScan(SitiWeb sw)}] ] ] ] ] \end{forest} \end{center} \paragraph{Piano di accesso fisico della query a con due indici} \begin{center} \begin{forest}, baseline, qtree [{Project(\{e.Titolo, sw.Url\})} [{IndexNestedLoop(e.IdEdizione = sw.IdEdizione)} [{IndexFilter(Edizioni e, IndETit,\\ e.Titolo $>=$ 'G' AND e.Titolo $<$ H)}] [{IndexFilter(SitiWeb sw, IndSWIdEd,\\ e.IdEdizione = sw.IdEdizione)}] ] ] \end{forest} \end{center} Indici necessari: \texttt{IndETit} (indice della tabella Edizioni sull’attributo Titolo) e \texttt{IndSWIdEd} (indice della tabella SitiWeb sulla chiave IdEdizione). \subsection{Query b} \paragraph{Piano di accesso logico della query b} \begin{center} \begin{forest} [{$\tau$(\{$-$TempoTotaleLettura\})} [{$\pi^{b}$ v.IdUtente, SUM(v.TempoLettura) TempoTotaleLettura} [{$\sigma$ SUM(v.TempoLettura) $>$ 10} [{\{v.IdUtente\} $\gamma$ \{SUM(v.TempoLettura)\}} [{$\sigma$ v.TempoLettura $<$ 60} [{Visite v}] ] ] ] ] ] \end{forest} \end{center} L’output dell’operatore $\gamma$ è già proiettato sulle sole dimensioni di analisi e risultati delle funzioni di aggregazione, non occorrerebbe proiettare su \texttt{\{v.IdUtente, SUM(v.TempoLettura)\}}; lo faccio per poter rinominare in TempoTotaleLettura il risultato della SUM. Nella $\tau$, l’ordinamento discendente è reso dal segno `$-$' per convenzione (in questo caso comunque il tipo è numerico). \paragraph{Piano di accesso fisico della query b senza indici} \begin{center} \begin{forest} [{Sort(\{$-$TempoTotaleLettura\})} [{Project(\{v.IdUtente, SUM(v.TempoLettura) TempoTotaleLettura\})} [{Filter(SUM(v.TempoLettura) $>$ 10)} [{GroupBy(\{v.IdUtente\}, \{SUM(v.TempoLettura)\})} [{Sort(\{v.IdUtente\})} [{Project(\{v.IdUtente, v.TempoLettura\})} [{Filter(v.TempoLettura $<$ 60)} [{TableScan(Visite v)}] ] ] ] ] ] ] ] \end{forest} \end{center} \clearpage \paragraph{Piano di accesso fisico della query b con indici} \begin{center} \begin{forest}, baseline, qtree [{Sort(\{$-$TempoTotaleLettura\})} [{Project(\{v.IdUtente, SUM(v.TempoLettura) TempoTotaleLettura\})} [{GroupBy(\{v.IdUtente\}, \{SUM(v.TempoLettura)\})} [{\sout{Project(\{v.IdUtente, v.TempoLettura\})}} [{IndexFilter(Visite v, IndVIdUt, v.TempoLettura $<$ 60)} ] ] ] ] ] ] ] \end{forest} \end{center} Indice necessario: \texttt{IndVIdUt} (indice della tabella Visite sull’attributo IdUtente). Non occorre ordinare prima di raggruppare: l’output di IndexFilter è già ordinato sull’insieme di attributi dell’indice (in questo caso \texttt{\{v.IdUtente\}}), che coincide nel nostro caso con l’insieme delle dimensioni di analisi. Non occorre nemmeno proiettare su \texttt{\{v.IdUtente, v.TempoLettura\}} prima del raggruppamento, in quanto la groupby può ricevere il record direttamente dalla IndexFilter e scartare gli attributi non rilevanti (cioè quelli che non sono dimensione di analisi né input delle funzioni di aggregazione). \vfill \subsection{Query c} \paragraph{Piano di accesso logico della query c} \begin{center} \begin{forest}, baseline, qtree [{$\pi$ a.IdArticolo, a.Titolo, COUNT(*) NumeroVisite,\\SUM(v.TempoLettura) TempoTotaleLettura} [{$\sigma$ COUNT(*) $>=$ 3} [{\{a.IdArticolo, a.Titolo\} $\gamma$ \{COUNT(*), SUM(v.TempoLettura)\}} [{$\bowtie$ a.IdArticolo = v.IdArticolo} [{$\sigma$ a.Premium = 'N'} [{Articoli a}] ] [{Visite v}] ] ] ] ] \end{forest} \end{center} \clearpage \paragraph{Piano di accesso fisico della query c senza indici} \begin{center} \begin{forest}, baseline, qtree [{Project(\{a.IdArticolo, a.Titolo, COUNT(*) NumeroVisite,\\SUM(v.TempoLettura) TempoTotaleLettura\})} [{Filter(COUNT(*) $>=$ 3)} [{GroupBy(\{a.IdArticolo, a.Titolo\}, \{COUNT(*), SUM(v.TempoLettura)\})} [{SortMerge(a.IdArticolo = v.IdArticolo)} [{Sort(a.IdArticolo)} [{Project(\{a.IdArticolo, a.Titolo\})} [{Filter(a.Premium = 'N')} [{TableScan(Articoli a)}] ] ] ] [{Sort(v.IdArticolo)} [{Project(\{v.IdArticolo, v.TempoLettura\})} [{TableScan(Visite v)}] ] ] ] ] ] ] \end{forest} \end{center} L’output di SortMerge è già ordinato per a.IdArticolo e, dato che \texttt{a.IdArticolo $\to$ a.Titolo}, non occorre ordinare prima della groupby. \paragraph{Piano di accesso fisico della query c con due indici} \begin{center} \begin{forest}, baseline, qtree [{Project(\{a.IdArticolo, a.Titolo, COUNT(*) NumeroVisite,\\SUM(v.TempoLettura) TempoTotaleLettura\})} [{Filter(COUNT(*) $>=$ 3)} [{GroupBy(\{a.IdArticolo, a.Titolo\}, \{COUNT(*), SUM(v.TempoLettura)\})} [{IndexNestedLoop(a.IdArticolo = v.IdArticolo)} [{Project(\{a.IdArticolo, a.Titolo\})} [{IndexFilter(Articoli a,\\ IndAIdA, a.Premium = 'N')} ] ] [{Project(\{v.IdArticolo, v.TempoLettura\})} [{IndexFilter(Visite v,\\IndVIdA, v.IdArticolo = a.IdArticolo)}] ] ] ] ] ] \end{forest} \end{center} Indici necessari: IndAIdA (indice della tabella Articoli sull’attributo IdArticolo), IndVIdA (indice della tabella Visite sull’attributo IdArticolo). Non occorre fare un ordinamento sull’output della IndexNestedLoop: avendo ricevuto input ordinato su IdArticolo dalla IndexFilter, e sapendo che \texttt{a.IdArticolo $\to$ a.Titolo}, l’input della groupby è già raggruppato per le dimensioni di analisi.