= Solution
Construct the required models and the actual customer–model purchases using <relational projection> and a <natural join>:
$$
M=\pi_{\mathsf{model}}C,\qquad I=\pi_{\mathsf{customerid}}U,\qquad V=\pi_{\mathsf{customerid,model}}(S\bowtie C).
$$
The <Cartesian product> $I\times M$ lists every obligation: every customer paired with every model present in `Car`. The <relational difference> $(I\times M)-V$ consists of unfulfilled obligations. Remove the customer identifiers occurring in this <database relation> and only then recover names:
$$
\begin{aligned}
G&=I-\pi_{\mathsf{customerid}}\bigl((I\times M)-V\bigr),\\
&\boxed{\text{answer}=\pi_{\mathsf{name}}(U\bowtie G).}
\end{aligned}
$$
This is an explicit <relational division> construction with all customers as the candidate universe. If there are no models, there are no missing obligations, so every customer qualifies. A version starting solely from customers occurring in $V$ would incorrectly omit customers with no purchases in that empty-model case.
The quantified object here is the `model` <database attribute>, so two makes sharing a model value contribute one required value. If a different application identified a model by the pair `(make, model)`, both the required and purchased <relational projections> would use that pair. Also, `Car` contains unsold cars: a model available only in unsold inventory is still a required model, and then nobody qualifies. Testing identifiers before names prevents different customers with the same name from pooling their purchases.
Back to article page