Ecto.Query.subquery
You're seeing just the function
subquery
, go back to Ecto.Query module for more information.
Converts a query into a subquery.
If a subquery is given, returns the subquery itself.
If any other value is given, it is converted to a query via
Ecto.Queryable
and wrapped in the Ecto.SubQuery
struct.
subquery
is supported in from
, join
, and where
, in the
form p.x in subquery(q)
.
Examples
# Get the average salary of the top 10 highest salaries
query = from Employee, order_by: [desc: :salary], limit: 10
from e in subquery(query), select: avg(e.salary)
A prefix can be specified for a subquery, similar to standard repo operations:
query = from Employee, order_by: [desc: :salary], limit: 10
from e in subquery(query, prefix: "my_prefix"), select: avg(e.salary)
Subquery can also be used in a join
expression.
UPDATE posts
SET sync_started_at = $1
WHERE id IN (
SELECT id FROM posts
WHERE synced = false AND (sync_started_at IS NULL OR sync_started_at < $1)
LIMIT $2
)
We can write it as a join expression:
set = from(p in Post,
where: p.synced == false and
(is_nil(p.sync_started_at) or p.sync_started_at < ^min_sync_started_at),
limit: ^batch_size
)
Repo.update_all(
from(p in Post, join: s in subquery(set), on: s.id == p.id),
set: [sync_started_at: NaiveDateTime.utc_now()]
)
Or as a where
condition:
subset = from(p in subset, select: p.id)
Repo.update_all(
from(p in Post, where: p.id in subquery(subset)),
set: [sync_started_at: NaiveDateTime.utc_now()]
)
If you need to refer to a parent binding which is not known when writing the subquery,
you can use parent_as
as shown in the examples under "Named bindings" in this module doc.