rmoretto
I’m trying to replicate the following PostgreSQL query with ecto:
SELECT *
FROM (SELECT * FROM event LIMIT 1000) as e
LEFT JOIN event_runner as er ON e.id = er.event_id
The idea is to limit only the FROM part of the query, returning the correct amount of events stopping the results from the JOINs interfering with the limit count, as it would occur if I used a LIMIT clause in the main query, as there is multiple event_runners for one event, defined as a has_many in the Event schema.
That said I trying to use the current ecto query:
event_query = from e in Event, limit: ^max_rows
from f in subquery(event_query),
left_join: er in assoc(f, :event_runners),
preload: [event_runners: er]
However when I run the above query I get the following error:
** (Ecto.QueryError) can only preload sources with a schema (fragments, binary and subqueries are not supported) in query:
from e0 in subquery(from f0 in MyApp.Event,
limit: 100,
select: f0),
join: e1 in MyApp.EventRunner,
on: e1.event_id == e0.id,
select: e0,
preload: [event_runners: er]
The error is clear, I can’t do a preload with subquery as a source schema, but the use of preload from the join part would be ideal, as we are receiving a lot of request and for each entry returned a separated query is executed if we use the Repo.preload function.
Is there any way to get around this limitation? Or, is it possible to rewrite this piece of code to make the limit only affect the FROM clause?
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










First 9 of 9 Posts
joey_the_snake
Postgres is smart enough not to join the entire tables before taking the limit so you can get rid of the subquery and just put the limit on the outside. At least it works on my version of postgres. Try out these 2 queries in a safe environment and compare how long they take to run
SELECT * from event e LEFT JOIN event_runner er on e.id = er.event_id LIMIT 1SELECT * from event e LEFT JOIN event_runner er on e.id = er.event_idIf limit is happening after the entire tables are joined then they should take the same time. In my testing the first is happening instantaneously while the second takes a long time.
rmoretto
Hey, thanks for the response, which version of postgres are you using?
joey_the_snake
I am using Postgres 13.
But now I realized what I said only works if there is at most on event runner per event. Otherwise you’ll get 1000 results but not 1000 events if one event has more than one runner. What is the cardinality of the relationship ?
rmoretto
Should have stated the cardinality in the post, the
Eventschema has ahas_manyrelationship to theEventRunner. Oneeventcan have 0..nevent_runners.joey_the_snake
Ah then you might need to use the separate preload query. I know it’s not what you want but it’s supposed to be more efficient anyways for one to many relationships. Otherwise your main table will have duplicated rows sent from the DB to Elixir.
joey_the_snake
Or if you’re deadset against a separate preload query you would have to do some kind of homegrown solution like this
Then perform the aggregation yourself on the results
rmoretto
Correct me if I’m wrong, but using the
Repo.preload(events, :event_runner)in a list of 1000Events, wouldn’t thepreloadrun 1000 queries to retrieve allEventRunners data? Looking at the application logs withlevel: :debug, it seems that this is the case.joey_the_snake
It would only run one query, something like
select * from event_runner where event_id in (....).rmoretto
That is true, I was inspecting the wrong logs, thanks for the help!