I am trying to see the SQL behind and AREL statement:
Brand.where(:subdomain => "coke").includes(:products).to_sql
brand has_many products, and product belongs_to brand.
However, the above statement yields only:
"SELECT `brands`.* FROM `brands` WHERE `brands`.`subdomain` = 'coke'"
Why don't I see the information for the products table in the SQL?
When you use an includes statement, Rails will generate a separate query per table (so if you include 2 other tables, there will be 3 queries total).
You can use a joins statement instead and it will lump it all into one query, however you may experience a performance hit. Also, if any of your where(...) conditions query against the included table, it will be lumped into one query.
See this other similar question for more information on Rails' behavior.
Related
I am trying to use where query with relationships.
How can I query using where with relations in this case?
This is model
User
has_many :projects
has_many :reasons, through: :projects
Project
belongs_to :user
has_many :reasons
Reasons
belongs_to :project
This is the codes which doesn't work
# GET /reasons
def index
reasons = current_user.reasons
updated_at = params[:updated_at]
# Filter with updated_at for reloading from mobile app
if updated_at.present?
# This one doesn't work!!!!!!!!!!!!
reasons = reasons.includes(:projects).where("updated_at > ?", Time.at(updated_at.to_i))
# Get all non deleted objects when logging in from mobile app
else
reasons = reasons.where(deleted: false)
end
render json: reasons
end
---Update---
This is correct thanks to #AmitA.
reasons = reasons.joins(:project).where("projects.updated_at > ?", Time.at(updated_at.to_i))
If you want to query all reasons whose projects have some constraints, you need to use joins instead of includes:
reasons = reasons.joins(:project).where("projects.updated_at > ?", Time.at(updated_at.to_i))
Note that when both includes and joins receive a symbol they look for association with that precise name. That's why you can't actually do includes(:projects), but must do includes(:project) or joins(:project).
Also note that the constraints on joined tables specified by where must refer to the table name, not the association name. That's why I used projects.updated_at (in plural) rather than anything else. In other words, when calling the where method you are in "SQL domain".
There is a difference between includes and joins. includes runs a separate query to load the dependents, and then populates them into the fetched active record objects. So:
reasons = Reason.where('id IN (1, 2, 3)').includes(:project)
Will do the following:
Run the query SELECT * FROM reasons WHERE id IN (1,2,3), and construct the ActiveRecord objects Reason for each record.
Look into each reason fetched and extract its project_id. Let's say these are 11,12,13. Then run the query SELECT * FROM projects WHERE id IN (11,12,13) and construct the ActiveRecord objects Project for each record.
Pre-populate the project association of each Reason ActiveRecord object fetched in step 1.
The last step above means you can then safely do:
reasons.first.project
And no query will be initiated to fetch the project of the first reason. This is why includes is used to solve N+1 queries. However, note that no JOIN clauses happen in the SQLs - they are separate SQLs. So you cannot add SQL constraints when you use includes.
That's where joins comes in. It simply joins the tables so that you can add where constraints on the joined tables. However, it does not pre-populate the associations for you. In fact, Reason.joins(:project), will never instantiate Project ActiveRecord objects.
If you want to do both joins and includes, you can use a third method called eager_load. You can read more about the differences here.
I have a simple database with the following schema:
Book has many Tags through Taggings
Book has many Users through ReadingStatuses
What I want to do is to list all of the books, their tags, and a reading status of the currently logged in user with each book. I've managed to write this using Arel (with the arel-helpers gem), but I don't know how to access the results in each book entry while iterating over the books array.
Here's the query
join_params = Book.arel_table.join(ReadingStatus.arel_table, Arel::OuterJoin)
.on(Book[:id].eq(ReadingStatus[:book_id])
.and(ReadingStatus[:user_id].eq(User.first.id)))
.join_sources
books = Book.all.includes(:tags).joins(join_params)
and the respective SQL it generates
SELECT "books".* FROM "books"
LEFT OUTER JOIN "reading_statuses"
ON "books"."id" = "reading_statuses"."book_id"
AND "reading_statuses"."user_id" = 'XXX'
There's nothing really to be done with the tags, since includes will automatically make everything work when calling book.tags, but what I don't know is how to access the ReadingStatus that is joined to each Book when iterating over the books result?
Try using the includes instead of joins. "includes" does eager fetching, but if you don't mind that it might make you query look a lot simpler.
You will also not have to explicitly mention the left outer join.
See if that helps:
Pulling multiple levels of data efficiently in Rails
Rails: How to fetch records that are 2 'has_many' levels deep?
Eager loading
Make sure you include the call to references
"ReadingStatus[:user_id].eq(User.first.id)" can be shifted into the where clause
I am using Ruby on Rails 3.1.10 in developing a web application.
Objective is to find all users that a user is following.
Let there be two models User and Following
In User model:
has_many :following_users, :through => :followings
When calling user.following_users, rails help generates a query that INNER JOIN between users and followings table by its magical default.
When users table has over 50,000 records while followings table has over 10,000,000 records, the inner join generated is resource demanding.
Any thoughts on how to optimize the performance by avoiding inner joining two big tables?
To avoid a single query with inner join, you can do 2 select queries by using the following method
# User.rb
# assuming that Following has a followed_id column for user that is being followed
def following_users_nojoin
#following_users_nojoin ||= User.where("id IN (?)", followings.map(&:followed_id))
end
This will not create a join table but would make two sql queries. One to get all the followings that belong to the user (unless it is already in the cache) and second query to find all the followed users. A user_id index on following, as suggested in the comment, would speed up the first query where we get all the followings for the user.
The above method would be faster than a single join query if the followings of a user have already been retrieved.
Read this for details on whether it is faster to make multiple select queries over a single query with join. The best way to find out which one is faster is to benchmark both methods on your production database.
Rails auto-generated a join table in this relationship :
# User.rb
has_and_belongs_to :topics
# Topic.rb
has_and_belongs_to :users
I want to query the join table, topics_users directly for a to obtain an ID's.
This strategy I feel would be the fastest at getting the User ID's from the Topics, rather than looking up the Join Table, looking up the Users, and Getting the ID's from the users.
If you really want to you can execute manual sql queries as follows:
ActiveRecord::Base.connection.execute("SELECT id FROM users_topics WHERE etc")
Is eager fetch same as join fetch?
I mean whether eagerly fetching a has-many relation fires 2 queries or a single join query?
How does rails active record implement a join fetch of associations as it doesnt know the table's meta-data in first hand (I mean columns in the table)? Say for example i have
people - id, name
things - id, person_id, name
person has one-to-many relation with the things. So how does it generate the query with all the column aliases even though it cannot know it when i do a join fetch on people?
An answer hasn't been accepted so I will try to answer your questions as I understand them:
"how does it know all the fields available in a table?"
It does a SQL query for every class that inherits from ActiveRecord::Base. If the class is 'Dog', it will do a query to find the column names of the table 'dogs'. In production mode it should only do this query once per run of the server -- in development mode it does it a lot. The query will differ depending on the database you use, and it is usually an expensive query.
"Say if i have a same name for column in a table and in an associated table how does it resolve this?"
If you are doing a join, it generates sql using the table names as prefixes to avoid ambiguities. In fact, if you are doing a join in Rails and want to add a condition (using custom SQL) for name, but both the main table and join table have a name column, you need to specify the table name in your sql. (e.g. Human.join(:pets).where("humans.name = 'John'"))
"I mean whether eagerly fetching a has-many relation fires 2 queries or a single join query?"
Different Rails versions are different. I think that early versions did a single join query at all times. Later versions would sometimes do multiple queries and sometimes a single join query, based on the realization that a single join query isn't always as performant as multiple queries. I'm not sure of the exact logic that it uses to decide. Recently, in Rails 3, I am seeing multiple queries happening in my current codebase -- but maybe it sometimes does a join as well, I'm not sure.
It knows the columns through a type of reflection. Ruby is very flexible and allows you to build functionality that will be used/defined during runtime and doesn't need to be stated ahead of time. It learns the associated "person_id" column by interpreting the "belongs_to :person" and knowing that "person_id" is the field that would be associated and the table would be called "people".
If you do People.includes(:things) then it will generate 2 queries, 1 that gets the people and a second that gets the things that have a relation to the people that exist.
http://guides.rubyonrails.org/active_record_querying.html