Group and count active record Rails - ruby-on-rails

I have two tables, users and votations, in votations have two fields win_id and user_id.
I need create query in active record with win_id (votation) and id (users).
My sql query like this:
Select users.name, Count(votations.win_id) from votations Inner Join users ON vontations.win_id = users.id Group By users.name;

Assuming you have an association in Violation like this:
belongs_to :user, foreign_key: "win_id"
Then this should do it:
Violation.joins(:user).group("users.name").count
It will give you a hash with the user name as the key and the count as the value.
If you don't have that association and don't want it, then change the joins() to this:
joins("JOIN users ON violations.win_id = users.id")

The clearest way may be to just use find_by_sql (docs here):
User.find_by_sql("Select users.name, Count(votations.win_id) from votations Inner Join users ON vontations.win_id = users.id Group By users.name")

Related

How to join on the same table multiple times?

I'm querying for the mutual friends of a given two users. The query below should do the trick for the most part and the friendship table should be self-evident, containing a user_id and friend_id.
SELECT `users`.* FROM `users`
INNER JOIN `friendships` `a` ON `users`.`id` = `a`.`friend_id`
INNER JOIN `friendships` `b` ON `users`.`id` = `b`.`friend_id`
WHERE `a`.`user_id` = 1 AND `b`.`user_id` = 2
What's got me confused is how to write this semantic ActiveRecord. With ActiveRecord you can join on an association, but only once. So how do you go about writing this as plainly as possible in ActiveRecord?
I do it with string arguments to joins:
User.
joins("INNER JOIN friendships a ON users.id = a.friend_id").
joins("INNER JOIN friendships b ON users.id = b.friend_id").
where("a.user_id" => 1, "b.user_id" => 2)
I'm not aware of a higher-level way to do such a join with Active Record.
Firstly, you should have proper Model Relationship between User & Friendship.
user model:
has_many :friendships
friendship model:
belongs_to :user
with that:
you can get activerecords in your iteration like:
users = User.all
users.each do |user|
user.friendships
.....
end
Or, by specific user, like:
user = User.first
user.friendships #returns association
or
User.first.friendships
for 2 specific users (given), you can do like:
User.where(id: [1,2]) #array of user id, then get the friendship record as above.
Hope this helps!

How to do this PG query in Rails?

I have the following simple relations:
class Company
has_many :users
end
class User
belongs_to :company
has_and_belongs_to_many :roles
end
class Role
has_and_belongs_to_many :users
end
The only column that matters is :name on Role.
I'm trying to make an efficient PostgreSQL query which will show a comma separated list of all role_names for each user.
So far I have got it this far, which works great if there's only single role assigned. If I add another role, I get duplicate users. Rather than trying to parse this after, I'm trying to just get it to return a comma separated list in a role_names field by using the string_agg() function.
This is my query so far and I'm kind of failing at taking it this final step.
User.where(company_id: id)
.joins(:roles)
.select('distinct users.*, roles.name as role_name')
EDIT
I can get it working via raw SQL (gross) but rails doesn't know how to understand it when I put it in ActiveRecord format
ActiveRecord::Base.connection.execute('SELECT users.*, string_agg("roles"."name", \',\') as roles FROM "users" INNER JOIN "roles_users" ON "roles_users"."user_id" = "users"."id" INNER JOIN "roles" ON "roles"."id" = "roles_users"."role_id" WHERE "users"."company_id" = 1 GROUP BY users.id')
User.where(company_id: id)
.joins(:roles)
.select('users.*, string_agg("roles"."name" \',\')')
.group('users.id')
Looks to me that you want to do:
User.roles.map(&:name).join(',')
(In my opionion SQL is a better choice when working with databases but when you are on rails you should probably do as much as possible with Active Record. Be aware of performance issues!)

ActiveRecord Rails: Get records using a condition in the relation

Here is an example: a User has many Cars and a Car belongs to a User.
I would like to extract all cars information, but not the cars of some users (this is because I would like to remove me and some colleagues to create correct stats).
I tried this, but without success:
Car.where("car.user.name != 'john'")
Any idea? Do you have a general rule about getting records with conditions in the relation?
Thanks.
Try the follwing:
Car.where('user_id != ?', User.find_by_<name?>('john').id
If you have rails 4:
Car.where.not(user_id: User.find_by(name: 'john')).id
UPDATE:
The solution above will work because you have a foreign key you can query against. More general solution is to perform left join with association table and filter those results. THe following will work regardless of association type (including has_many :through and has_and_belongs_to_many):
Car.includes(:user).where('users.name != ?', 'john')
# rails 4
Car.includes(:user).where.not(users: { name: 'john'})
Car.includes(:users).where("users.name NOT IN ('bill','peter','joe') OR cars.user_id IS NULL")
It will return all cars that don't have a user name that is Bill, Peter or Joe, and also cars that don't belong to a user !
Have a try with
Car.joins([:user]).where("users.name != 'John'")
If you need all the cars information with user information like users.name, then you can have
Car.joins([:user]).select('cars.*, users.name AS user_name').where("users.name != 'John'")
The above does the thing with a single sql query
SELECT cars.*, users.name AS user_name FROM `cars` INNER JOIN `users` ON `users`.`id` = `cars`.`user_id` WHERE (users.name != 'John')

ActiveRecord where at least x records of association exist

Let's say users have comments and I want all users with three comments or more.
User.joins(:comments) will get me any user that has
one or more comments. What is the nicest way to get Users with at least three comments?
A nicer way might be to write that subquery using the API:
subquery = Comment.select("user_id").
group(:user_id).
having("COUNT(*) >= 3").to_sql
User.where("id IN (#{subquery})")
SQL like this:
SELECT users.* FROM users
WHERE EXISTS (SELECT id FROM comments WHERE user_id = users.id GROUP BY user_id HAVING COUNT(*) >= 3)
Which in ActiveRecord notation translates to:
User.where('EXISTS (SELECT id FROM comments WHERE user_id = users.id GROUP BY user_id HAVING COUNT(*) >= 3)')

How to write query in active record to select from two or more tables in rails 3

I don't want to use join
I want to manually compare any field with other table field
for example
SELECT u.user_id, t.task_id
FROM tasks t, users u
WHERE u.user_id = t.user_id
how can i write this query in Rails ??
Assuming you have associations in your models, you can simply do as follow
User.joins(:tasks).select('users.user_id, tasks.task_id')
you can also do as follow
User.includes(:tasks).where("user.id =tasks.user_id")
includes will do eager loading check the example below or read eager loading at here
users = User.limit(10)
users.each do |user|
puts user.address.postcode
end
This will run 11 queries, it is called N+1 query problem(first you query to get all the rows then you query on each row again to do something). with includes Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
Now when you do;
users = User.includes(:address).limit(10)
user.each do |user|
puts user.address.postcode
end
It will generate just 2 queries as follow
SELECT * FROM users LIMIT 10
SELECT addresses.* FROM addresses
WHERE (addresses.user_id IN (1,2,3,4,5,6,7,8,9,10))
Plus if you don't have associations then read below;
you should be have to look at http://guides.rubyonrails.org/association_basics.html
Assuming your are trying to do inner join, by default in rails when we associate two models and then query on them then we are doing inner join on those tables.
You have to create associations between the models example is given below
class User
has_many :reservations
...# your code
end
And in reservations
class Reservations
belongs_to :user
... #your code
end
Now when you do
User.joins(:reservations)
the generated query would look like as follow
"SELECT `users`.* FROM `users` INNER JOIN `reservations` ON `reservations`.`user_id` = `users`.`id`"
you can check the query by doing User.joins(:reservations).to_sql in terminal
Hopefully it would answer your question
User.find_by_sql("YOUR SQL QUERY HERE")
You can use as follows..
User.includes(:tasks).where("user.id =tasks.user_id").order(:user.id)

Resources