Ruby on Rails Active Record inner join not working - sql

I have 2 models: Engagement, user. Engagement basically refers to the items booked by the user. each engagement is for a specific user. user in engagement has a foreign key. I want to join engagement with user so that i can see the details of the user.
Here are my models
class Engagement < ActiveRecord::Base
belongs_to :food_item
belongs_to :user
end
class User < ActiveRecord::Base
has_many :engagements
has_many :food_item, through: :engagements
end
I am running below query:
Engagement.joins("INNER JOIN users ON users.id = engagements.user_id")
It is not joining both the tables.
Any help will be appreciated

Your query is right.
You're doing a inner join and only returning engagements that have a relation to user.
To return the user data you should do something like this: Engagement.select('tasks.*, users.*').joins(:user). This way the object will have engagement and user attributes. But that is not the Rails way.
The "correct" way is:
engagements = Engagement.includes(:user)
engagements.first.user # this will return the user data
This way you're getting all engagements and preloading the user data, this way avoiding n + 1 queries (Eager-loading) ;)

Try this Engagement.joins(:users)
It should work.

Related

Select users whose last associated object was created less than x days before

I am building a Rails 4.2.7.1 which uses Postgres database and I need to write a feature for certain group of users.
class User < ActiveRecord::Base
has_many :payments
end
class Payment < ActiveRecord::Base
belongs_to :user
end
I need to select users from certain location who have exactly one payment and I also need to be able to pick users whose payment created_at attribute is exactly x
I tried
location.users
.without_deleted
.where(num_payments: 1)
.joins(:payments)
.where('payments.user_id = users.id').order('created_at
DESC').where("payments.created_at < ?", Date.today).group('users.id')
but it did not give me expected results.
Thanks!
You should start from User since this is what you want at end, and take joins with payments since you want to query it along.
User.joins(:payments)
.where(location_id: location.id, num_payments: 1)
.where(payments: { created_at: Date.today })

How to select parent by most recent child with Activerecord

I have a user model which has many subscriptions. I need to make two selections:
active users defined as user with a subscription in the last month
inactive users the ones that don't meet the (1) criteria
My subscription model has a simple scope .latest which is defined as ordered("created_at DESC").first.
To make selection (1) I use:
User.joins(:subscriptions).where("subscriptions.created_at > ?", 1.month.ago).distinct
This works, no problem there. However, I can't seem to define a working query for selection (2). Currently I use selection (1) and 'subtract' that from User.all to get the remaining users. This feels a bit like a hack.
The selection I need is:
all users whose most recent subscription was created more than 1 month ago
It's the most recent part of the query that has me stuck.
Any help appreciated.
Quick & dirty way: use complex SQL like this
Assume you are using auto incremental ID
User.select("users.*, MAX(subscriptions.id) as last_subscription_id")
.joins(:subscriptions)
.group("subscriptions.user_id")
.having("last_subscription_id = (select id from subscriptions where user_id=users.id and created_at < '2017-10-01 09:23:28.182475' order by id desc limit 1)")
Recommended way
Add last_subscription_id to users table and setup a belongs_to relationship user belongs_to last_subscription then do the joins normally. You need to update last_subscription_id of an user when new subscription for this user is created too.
Example: User class looks like this (I include has_many :subscriptions to show that we have 2 relations now)
class User < ActiveRecord::Base
has_many :subscriptions
belongs_to :last_subscription, class_name: 'Subscription', foreign_key: :last_subscription_id
end
And query will be
User.joins(:last_subscription).where("subscriptions.created_at < ?", 1.month.ago)
for the most recent part you can do this.
User.joins("LEFT JOIN subscriptions ON subscriptions.user_id = users.id").order("subscriptions.created_at DESC").select("subscriptions.created_at AS max_date").group_by(&:id).select{|key, value| value[0].max_date < 1.month.ago}.values.flatten

Get distinct records with a join left

I know there are a lot of threads like that on SO but i still can't figure out what's wrong with my code. My models are:
User
class User < ActiveRecord::Base
has_many :events
end
Event
class Event < ActiveRecord::Base
belongs_to :user
belongs_to :venue
end
Venue
class Venue < ActiveRecord::Base
has_many :events
end
What i'm trying to do is to get all the recent venues used by a user (through the events he have created). My activerecord chaing is:
User
.first
.events
.joins(:venue)
.select('venues.id, venues.name, venues.address, events.beginning_at')
.uniq('venues.id')
and it produces the following query:
SELECT DISTINCT(venues.id), venues.name, venues.address, events.beginning_at
FROM "events" INNER JOIN "venues" ON "venues"."id" = "events"."venue_id"
WHERE "events"."user_id" = $1
ORDER BY "events"."beginning_at" ASC [["user_id", 1]]
However, what it gives me is a quite "randomish" list of recently used venues. ATM all the events are assigned to the same venue and the query above gives me ALL of those venues (so it goes down to one venue multiplied by number of all events that user created). What's more interesting - that query gives me 9 results but when i remove the DISTINCT method, i get 20 results (while there is just one venue in my db)
Can you give me any tip on what am i doing wrong?

Active Record: Find collection based on sum of two associated tables

I am trying to find the collection of 'underpaid' events in our system. We are running Rails 3.2 using a Postgres database.
The data structure is as follows.
class Event < ActiveRecord::Base
has_many :charges
has_many :transactions
end
class Charge < ActiveRecord::Base
belongs_to :event
end
class Transaction < ActiveRecord::Base
belongs_to :event
end
Underpaid events are defined as those where the total charges are greater than total transactions.
sum(charges.total) > sum(transactions.total)
My SQL skills are poor and I have been trying to execute this using ActiveRecord. This is my latest attempt but it is not bringing back the right collection. In particular it seems to include fully paid events where there was more than one transaction.
Event.joins(:charges,:transactions).group('charges.event_id, transactions.event_id').having("sum(charges.total) > sum(transactions.total)")
Is it possible to achieve this in ActiveRecord and if so, how can I go about it?
Hey I think that in SQL it should be like that
select * from events where
(select sum(charges.total) from charges where charges.event_id = events.id) >
(select sum(transactions.total) from transactions where
transactions.event_id = events.id)
so for now you can build scope like
scope :unpaid, find_by_sql("select * from events where
(select sum(charges.total) from charges where charges.event_id = events.id) >
(select sum(transactions.total) from transactions where
transactions.event_id = events.id)")
I hope it will help!

Query a 3-way relationship in Active Record

I'm trying to figure out how to query this relationship without using find_by_sql
class User < ActiveRecord::Base
has_many :lists
end
class List < ActiveRecord::Base
has_many :list_items
belongs_to :user
end
class ListItem < ActiveRecord::Base
belongs_to :list
belongs_to :item
end
class Item < ActiveRecord::Base
has_many :list_items
end
this should be what we are using but How would I do this not by find_by_sql
in user.rb
def self.find_users_who_like_by_item_id item_id
find_by_sql(["select u.* from users u, lists l, list_items li where l.list_type_id=10 and li.item_id=? and l.user_id=u.id and li.list_id=l.id", item_id])
end
I've tried several different includes / joins / merge scenarios but am not able to get at what I'm trying to do.
thx
It's a bit difficult to tell exactly what query you're trying to do here, but it looks like you want the user records where the user has a list with a particular list_type_id and containing a particular item. That would look approximately like this:
User.joins(:lists => [:list_items]).where('lists.list_type_id = ? and list_items.item_id = ?', list_type_id, item_id)
This causes ActiveRecord to execute a query like the following:
SELECT "users".* FROM "users" INNER JOIN "lists" ON "lists"."user_id" = "users"."id" INNER JOIN "list_items" ON "list_items"."list_id" = "lists"."id" WHERE (lists.list_type_id = 10 and list_items.item_id = 6)
and return the resulting collection of User objects.