When optimizing relational algebra expressions, the goal is not only logical correctness but also minimization of intermediate result sizes. This is especially critical when one relation is small and others are large.
Instance:
$$
\texttt{branch:}
\quad
\begin{array}{|c|c|c|}
\hline
\texttt{b-name} & \texttt{b-city} & \texttt{assets} \\
\hline
B1 & \texttt{Agra} & 500 \\
B2 & \texttt{Delhi} & 600 \\
B3 & \texttt{Mumbai} & 700 \\
\hline
\end{array}
$$
$$
\texttt{account:}
\quad
\begin{array}{|c|c|c|}
\hline
\texttt{a-num} & \texttt{b-name} & \texttt{bal} \\
\hline
A1 & B1 & -100 \\
A2 & B1 & 200 \\
A3 & B1 & 300 \\
A4 & B2 & -50 \\
A5 & B2 & -60 \\
A6 & B3 & -70 \\
\hline
\end{array}
$$
$$
\texttt{depositor:}
\quad
\begin{array}{|c|c|}
\hline
\texttt{c-name} & \texttt{a-num} \\
\hline
C1 & A1 \\
C2 & A2 \\
C3 & A3 \\
C4 & A4 \\
C5 & A5 \\
C6 & A6 \\
\hline
\end{array}
$$
Correct result: Only account $A1$ is in "Agra" and has $\texttt{bal < 0}$ → output: $\{C1\}$.
Now evaluate all options.
Option A:
$$
\Pi_{\texttt{c-name}} \left( \sigma_{\texttt{bal < 0}} \left( \sigma_{\texttt{b-city = "Agra"}}(\texttt{branch}) \bowtie \texttt{account} \right) \bowtie \texttt{depositor} \right)
$$
Steps:
- $\sigma_{\texttt{b-city="Agra"}}(\texttt{branch}) = \{(B1, \texttt{Agra}, 500)\}$
- Join with $\texttt{account}$ → accounts at B1: $A1, A2, A3$ → 3 tuples
- $\sigma_{\texttt{bal < 0}}$ → only $A1$ → 1 tuple
- Join with $\texttt{depositor}$ → $C1$
- Project → $\{C1\}$
Correct Result. Peak intermediate size: 3 tuples (after step 2).
Option B:
$$
\Pi_{\texttt{c-name}} \left( \sigma_{\texttt{b-city = "Agra"}}(\texttt{branch}) \bowtie \left( \sigma_{\texttt{bal < 0}}(\texttt{account}) \bowtie \texttt{depositor} \right) \right)
$$
Steps:
- $\sigma_{\texttt{bal < 0}}(\texttt{account}) = \{A1, A4, A5, A6\}$ → 4 tuples
- Join with $\texttt{depositor}$ → $\{C1, C4, C5, C6\}$ → 4 tuples
- $\sigma_{\texttt{b-city="Agra"}}(\texttt{branch}) = \{(B1, \texttt{Agra}, 500)\}$
- Final join on $\texttt{b-name}$: only $A1$ (B1) matches → $C1$
- Project → $\{C1\}$
Correct Result: Peak intermediate size: 4 tuples (after step 2).
Even in this tiny instance, Option B processes more data: 4 vs. 3 tuples. The gap widens dramatically at scale.
Option C:
$$
\Pi_{\texttt{c-name}} \left( \left( \sigma_{\texttt{b-city = "Agra"}}(\texttt{branch}) \bowtie \sigma_{\texttt{b-city = "Agra"} \land \texttt{bal < 0}}(\texttt{account}) \right) \bowtie \texttt{depositor} \right)
$$
Selection $\sigma_{\texttt{b-city = "Agra"} \land \texttt{bal < 0}}(\texttt{account})$ is invalid: $\texttt{account}$ has no $\texttt{b-city}$.
Semantically incorrect.
Option D:
$$
\Pi_{\texttt{c-name}} \left( \sigma_{\texttt{b-city = "Agra"}}(\texttt{branch}) \bowtie \left( \sigma_{\texttt{b-city = "Agra"} \land \texttt{bal < 0}}(\texttt{account}) \bowtie \texttt{depositor} \right) \right)
$$
Same error: $\texttt{b-city}$ not in $\texttt{account}$.
Semantically incorrect.
Scalability Insight from This Instance
In the example:
- Agra branch has 3 accounts,
- But 4 accounts in total have $\texttt{bal < 0}$ (including 3 outside Agra).
Thus:
- Option A never sees the 3 non-Agra negative accounts,
- Option B processes all 4, then discards 3 in the final join.
Now scale up:
- Suppose 100 branches, 1,000,000 accounts,
- Agra: 10,000 accounts,
- Negative balance: 100,000 accounts (only 1,000 in Agra).
Then:
- Option A intermediate after join: 10,000 → after filter: 1,000
- Option B intermediate after filter: 100,000 → after depositor join: ~200,000
- The 20$\times$ blowup in Option B arises directly from the pattern visible even in our small instance.
Therefore
- Options C and D are invalid due to attribute misuse.
- Options A and B are correct, but Option A minimizes intermediate results by using the small $\texttt{branch}$ relation to restrict $\texttt{account}$ early.
- The constructed instance explicitly shows that Option B processes more tuples even at small scale, and this difference scales linearly with data size.
Therefore, Option A is the most efficient.