How to write this Join Constraint with ActiveRecord - ruby-on-rails

How should I write this with ActiveRecord:
select stores.id, stores.name, store_specials.active
from stores
left outer join store_specials on
(store_specials.store_id = stores.id and store_specials.special_id = 1)
where stores.active = true;
Thanks

Rails 3.x
Stores.select("stores.id, stores.name, store_specials.active").joins("LEFT OUTER JOIN tore_specials ON store_specials.store_id = stores.id AND store_specials.special_id = 1).where("store_specials.active = true")
Hopefully this is what you need

Related

How to left outer joins with conditions

I have this relation:
class Action < ApplicationRecord
has_many :actions_users
I tried to make a query like:
select *
from actions left outer join actions_users
on actions_users.action_id = actions.id and actions_users.user_id = 1
where actions.user_id = 1
Meanwhile, in my experience, in all of the result that I tried,
select *
from actions left outer join actions_users
on actions_users.action_id = actions.id
where actions.user_id = 1 and actions_users.user_id = 1
the join condition code and general condition are in where function.
How can I work it out?
You can pass a string in join query and use the rails table naming conventions for this.
Action.joins("left outer join action_users on (action_users.id = actions.id and action_users.id = 1")).where('action_users.user_id = ? ', 1)
Because you have a general where condition, you can use includes. This will generate a LEFT OUTER JOIN query:
Action.includes(:actions_users).where(actions_users: { user_id: true })
Or if you are using Rails 5+, Active Record provides a finder method left_outer_joins:
Action.left_outer_joins(:actions_users).where(actions_users: { user_id: true })
Action.left_outer_joins(:actions_users).where(user_id: 1)
select *
from actions left outer join actions_users
on actions_users.action_id = actions.id and actions_users.user_id = 1
where actions.user_id = 1
Although you did not ask for it yet, ...
Action.left_outer_joins(:actions_users).where(actions_users: {status: 'active'})
select *
from actions left outer join actions_users
on actions_users.action_id = actions.id and actions_users.user_id = 1
where actions_users.status = 'active'
Up to Rails 7 there is no way to specify conditions directly on an OUTER JOIN, see the documentation for Specifying Conditions on the Joined Tables. The examples shown are suitable for INNER JOINs (as they use .where), but won't work for OUTER JOINs for the same reason.
You could try to specify the OUTER JOIN manually, but will run into problems passing parameters:
Action.joins("LEFT OUTER JOIN action_users ON (action_users.id = actions.id AND action_users.id = :user_id")
So you will need to do parameter substitution somewhat like this:
outer_join_sanitized = ApplicationRecord.sanitize_sql([
"LEFT OUTER JOIN action_users ON (action_users.id = actions.id AND action_users.id = :user_id)",
{ user_id: 22 }
])
And you could then use Actions.joins(outer_join_sanitized). At this point you might agree that just running with raw SQL from the start is the easier way to go.

ActiveRecord rails 4 not equal to condition

I am using rails 4 for developing my application. I have a problem in my active record query with not equal to condition. I am not getting required output. I think there is a problem with not equal to part.
#available_rooms = Room.joins("LEFT OUTER JOIN reservations ON reservations.room_id = rooms.id").where("category_id = ? AND arrival_date != ?",params[:category],params[:arrival_date])
You can use where.not() in Rails 4 :
#available_rooms = Room.joins("LEFT OUTER JOIN reservations ON reservations.room_id = rooms.id")
.where(category_id: params[:category])
.where.not(arrival_date: params[:arrival_date])
In some version of SQL not equal is written as !=
Try this using the comparator <> like so:
#available_rooms = Room.joins("LEFT OUTER JOIN reservations ON reservations.room_id = rooms.id")
.where("category_id = ? AND arrival_date <> ?",params[:category],params[:arrival_date])

NOT EXISTS SQL query in rails 3.2

I have an sql query like this:-
SELECT * FROM `permissions` join entities where NOT EXISTS (select
entity_id,permission_id from role_permissions where role_id=5 and
entities.id = role_permissions.entity_id and permissions.id =
role_permissions.permission_id)
I would like to get the corresponding rails query.
I have tried this.
Permission.joins("join entities").joins("LEFT OUTER JOIN role_permissions on
permission_id != permissions.id and entities.id != entity_id and
role_permissions.role_id= role_id").select("role_permissions.entity_id,role_permissions.role_id,
role_permissions.permission_id").group('role_permissions.entity_id,
role_permissions.permission_id')
But it doesn't works.
thanks
hari
I have been particularly in love with EXISTS queries lately, precisely because it does not require you to fully join another table. As far as I know, you do have to explicitly write the SQL clause, but you can still make it work with Activerecord. You can even put this in a scope within a lambda block.
Permission.joins(:entities).where(<<-SQL
NOT EXISTS(
select
* from role_permissions where role_id=#{your_role_id} and
entities.id = role_permissions.entity_id
and permissions.id = role_permissions.permission_id
)
SQL
)
Try like this for a default SQL query:
sql = "SELECT some_field FROM `permissions` join entities where NOT EXISTS (select entity_id,permission_id from role_permissions where role_id=5 and entities.id = role_permissions.entity_id and permissions.id = role_permissions.permission_id)"
results = ActiveRecord::Base.connection.execute(sql)
results.each do |result|
#register_users << { some_field: result[0] }
end
Try this
UPDATED BASED ON FIRST COMMENT
Permission.joins(:entities).joins("LEFT OUTER JOIN role_permissions on
permission_id != permissions.id and entities.id != entity_id and
role_permissions.role_id= role_id")
.select("role_permissions.entity_id,role_permissions.role_id,
role_permissions.permission_id")
.group('role_permissions.entity_id, role_permissions.permission_id')

Rails Nested Query

I have follow query
notes = Note.where('notes.id IN
(
SELECT "notes"."id" FROM "notes"
WHERE "notes"."circle_id" = ?
)
OR notes.id IN
(
SELECT "notes"."id" FROM "notes"
INNER JOIN "circles_dreams"
ON "circles_dreams"."dream_id" = "notes"."dream_id"
WHERE "circles_dreams"."circle_id" = ?
)', #circle.id, #circle.id)
How to simplify this query?
Thanks.
First of all you can collect all needed notes id.
I supposed to think what you already have relations between Note and CirclesDream
note_ids = Note.where(circle_id: #circle.id).pluck(:id) # your first SELECT
dream_ids = CirclesDream.where(id: #circle.id).pluck(:note_id) # your second SELECT
notes_ids = note_ids | dreams_ids # combine them
notes = Note.where(id: notes_ids) # Now your
upd: I've just fixed typo. Changed id to note_id in second request
Try this
note_ids = Note.where('circle_id = ?', #circle.id).pluck(:id)
dream_note_ids = Note.joins(:circle_dreams).where('circle_dreams.circle_id = ?', #circle.id).plunk(:note_id)
notes_ids = note_ids | dream_note_ids
If your circle_dreams table can contain records having note_id = null, then you have to apply join. So i think this will work in your case....

Self joins in Rails

I have the following tables: users(id), articles(id), favorites(article_id, user_id). I need to list articles added to favorites by current user with a flag indicating if they were also favorited by other users. SQL is quite simple:
select articles.id, count(f2.article_id)
from articles a
inner join favorites f1 on f1.article_id = a.id
left join favorites f2 on f1.article_id = f2.article_id and not f1.user_id = f2.user_id
where f1.user_id = 1
group by a.id
Is there a way to do it using Rails query generator?
something like this should get you on your way (assuming you have an Article and Favorites model/table):
t = Article.joins("INNER JOIN #{Favorite.table_name} AS f1 on f1.article_id = #{Article.table_name}.id")
t = t.joins("LEFT JOIN #{Favorite.table_name} AS f2 on f1.article_id = f2.article_id AND NOT f1.user_id = f2.user_id")
t = t.where("f1.user_id = ?", 1)
t = t.group("#{Article.table_name}.id")
t.select("#{Article.table_name}.id, COUNT(f2.article_id)")

Resources