And again, we have the option to do this via sorting or hashing:
\begin{itemize}
    \item \bi{Sorting}: Sort on the attribute. Then scan the sorted tuples, computing running aggregate (such as max/min, average, etc).
          When a new group is encountered, then we output the aggregate. The cost is \cost{$\texttt{cost}_\texttt{sort}(R) + \texttt{cnt}(R)$}.
          Alternatively, we can already compute the aggregates on the last step of sorting (the merge) to save some time.
          The limiting factor then becomes the sorting and the cost is \cost{$\texttt{cost}_\texttt{sort}(R)$}
    \item \bi{Hashing}: We hash the attribute and now each hash table entry is a group of all records with this value of the attribute.
          For each one of these groups, we compute the aggregate. The cost is \cost{$\texttt{cnt}(R)$}
          If the table is too large for memory, we use a two-step approach, as before. The cost is then very similar \cost{$\texttt{cnt}(R) + B$} for $B$ tables.
\end{itemize}
