Performance implications of Ecto within-query preloads with postgres

Correct. Keep in mind that this is often the best way to load one to many associations because of the way SQL database return data. If you have three tables, all associated in one to many, and each table returns respectively K, M, N entries, you will get overall K * M * N rows. That ends up with a lot of duplication, which translates to more data over the wire and more work decoding the data. When using preloads, you get K + M + N rows, which ends up being more performant.

If you want to force to actually go the join route, you can do so too:

from q in query, join: s in assoc(q, :species), join: sg in assoc(q, :service_gaps), preload: [species: s, service_gaps: sg]