We have seen how in()
can build server-side expressions that test whether the value of some subexpression
is equal to some of the results
of a subquery. Collective comparisons are more general. Instead of equality
we can choose any of the six relational operators; and we can choose to
ask whether some, or whether all,
of the subquery's results pass the test.
Caveats: collective comparisons are only available for PostgreSQL (this is checked at run time), and the series of comparands can only be given via a subquery, not directly in the commandline arguments, and not via a vector.
Collective comparisons are presented as overloads of the C++ relational
operarors ==, !=, <,
>, <=,
and >=. These overloads
apply when there is a mapped type T such that:
abstract_mapper<T> or a T,
and
all(q) or some(q), where q
is a query whose value type is T. The query
q is allowed to use mappers that belong
to an outer context, as was the case
with exists().
So, if we start with our example of in() with a subquery:
// Find points whose x is interesting: // extern const query<float> interesting_numbers; const query<point> interesting_wrt_x = points .where(in(points->x, interesting_numbers));
We can rewrite it as a collective comparison:
// Find points whose x is interesting: // const query<point> equal_some = points .where(points->x == some(interesting_numbers));
And now we can make variations:
// Find points whose x is below at least one interesting number: // const query<point> below_some = points .where(points->x < some(interesting_numbers)); // Find points whose x differs from every interesting number: // const query<point> unequal_all = points .where(points->x != all(interesting_numbers)); // Find points whose x exceeds every interesting number: // const query<point> above_all = points .where(points->x > all(interesting_numbers));
It would be a mistake to read these in the same way we usually read comparisons
in programming languages: “Evaluate LHS; evaluate RHS; apply comparison
operator between two results”. It would be a mistake because (a)
there is no such step as “evaluate RHS” here, and (b) there
is no sense in which a comparison operator is evaluated just once. Better
to understand them as loops. An expression of the form A OP
some(q) considers each output o
that q produces, and succeeds if A
OP o is ever true. An expression of the form A
OP all(q) considers each output o
that q produces, and succeeds if A
OP o is always true. (Of course this is subject to optimizations
inside the DBMS.)
For syntactic convenience, quince allows “some(q) OP' A”
and “all(q) OP' A”
as synonyms for “A OP some(q)” and “A OP
all(q)”, respectively, where OP'
is the converse relation to OP. E.g.
all(interesting_numbers) < points->x
is equivalent to:
points->x > all(interesting_numbers)