In Rails, you can write something like this:
Project.limit(10).each do |project|
project.members.count
end
Here `project.members` returns some sort of query object that produces a query along the lines of this:
[...] FROM members WHERE members.project_id = X [...]
In other words, you can just query associated data on a per-project basis, without needing to pass any additional arguments.
The problem this results in is that in the above code you'd run a COUNT query for _each_ project. Instead what you'd want is a single COUNT that groups data per project, such that you can then pass that data along with the `each` call. This setup would only require 2 queries, instead of 20.
To eager load data in Rails you have to explicitly opt-in, resulting in something like this:
Project.includes(:members).limit(10).each do |project|
project.members.count
end
The problem here is that an opt-in mechanism is too easy to forget (as is evident by how common these N+1 query problems are), and even if you include it there are certain cases where you still end up running extra queries for each row.
The solution here is to separate querying from the row instance types, e.g. a "Project" type can't query data itself and instead requires it to be passed in. This makes it much more difficult to create N+1 query problems.