ActiveRecord Query in another query, like "IN" with SQL - ruby-on-rails

i would like write this query in Rails/Active Record
SELECT * FROM messages WHERE (realtor_id,created_at) IN ( SELECT realtor_id, MAX(created_at),message FROM messages WHERE `messages`.`user_id` = 8 GROUP BY realtor_id );
I tried some syntaxes but it doesnt work...
I think, the solution is near :
#discussions = Message.where(realtor_id: created_at: [Message.where(:user_id => current_user.id).select("realtor_id, MAX(created_at) as created_at").group("realtor_id").order("created_at ASC")])
Someone can help me ?
Thanks for advance,
F.

You can create sql query and pass in
query = "SELECT * FROM messages WHERE (realtor_id,created_at) IN ( SELECT realtor_id, MAX(created_at),message FROM messages WHERE `messages`.`user_id` = 8 GROUP BY realtor_id )"
#discussions = ActiveRecord::Base.connection.execute(query)

Related

How to use dynamic array of ids in raw query (rails + postgres)?

I have this code
Language.all.map{|l|[l.language, l.accounts.joins(:children).where("accounts.id IN (?)", #accounts.ids).uniq.count]}
I am trying to get this as output
[["eng", 5] ["span", 3] ["ital", 4] ... ]
I want to write it as a raw query.
I tried this,
ActiveRecord::Base.connection.execute("select languages.language, (SELECT COUNT(*) FROM accounts where accounts.language_id = languages.id) from languages").values
but i need to pass accounts.ids dynamic.. like this
select languages.language, (SELECT COUNT(*) FROM accounts where accounts.language_id = languages.id AND (accounts.id IN (#{#accounts.ids})) from languages"
when i tried to pass accounts.id IN #{#accounts.ids} i am getting error
(accounts.id IN ([2839, 284.. .. this should have been IN (2839, 284..) instead, it is taking array.
How to pass it dynamically ?
You can try:
"... accounts.id IN (#{#accounts.ids.join(',')})"
Hope it helps.
The above 2 answers won't work if you are attempting to use an array of strings in a raw sql query. For a raw sql statement, you can use ActiveRecord's sanitize_sql_array method.
languages = ['eng', 'span', 'chin', 'ital']
base_query = "SELECT * FROM languages WHERE id IN (?)"
sanitized_query = ActiveRecord::Base.send :sanitize_sql_array, [base_query, languages]
ActiveRecord::Base.connection.execute(sanitized_query)
You can use where(id: #accounts.ids) or where("account.ids": #accounts.ids) or where("account.ids IN ?, #accounts.ids). I believe ActiveRecord should understand all of them.
http://guides.rubyonrails.org/active_record_querying.html#array-conditions
http://guides.rubyonrails.org/active_record_querying.html#subset-conditions

What's the right way to search a Rails model by multiple IDs in a more advanced query?

I'm using Rails 5 and PostGres 9.5. How do I write a Rails query that returns results based on finding multiple IDs? I have this
criteria = "my_objects.id IN ?"
param = "(#{params[:ids]})"
...
#results = MyObject.joins("LEFT OUTER JOIN addresses ON my_objects.address_id = addresses.id")
.where("#{criteria} AND EXISTS (SELECT * FROM my_object_times WHERE my_object_times.my_object_id = my_objects.id)", param)
.order(order_by)
.paginate(:page => params[:page])
The IDs are passed in via a query string parameter, taht looks something like
ids=9e24abc1-1422-4e51-9d0b-72ea444f8110,dcba2558-9bcc-48a2-b5ba-61b230aa796f
but the above results in the error
PG::SyntaxError: ERROR: syntax error at or near "'(9e24abc1-1422-4e51-9d0b-72ea444f8110,dcba2558-9bcc-48a2-b5ba-61b230aa796f)'"
Just pass an array as a parameterized value to the query:
ids = params[:ids].split(',')
#results = MyObject.joins("LEFT OUTER JOIN addresses ON my_objects.address_id = addresses.id")
.where("my_objects.id IN ? AND EXISTS (SELECT * FROM my_object_times WHERE my_object_times.my_object_id = my_objects.id)", ids)
.order(order_by)
.paginate(:page => params[:page])
Additionally you should be able to replace AND EXISTS by doing a INNER LEFT join:
#results.left_outer_joins(:addresses)
.joins(:my_object_times)
.where(id: ids)
left_outer_joins is new in Rails 5.

Right way to union in rails 3.1.4 with sqlite3?

There are 3 tables payment_logs, sourcings and purchasings in our rails app. A payment_log belongs to either sourcing or purchasing but not both at the same time. There is a col project_id in both sourcing and purchasing. We want to pick up all payment_logs with its project_id = project_id_search (project_id_search passed from a search page). Also we need a ActiveRecord as resultset returned. Here is the individual query, assuming payment_logs holds the ActiveRecord result set:
pick all payment_logs with its sourcing's project_id = project_id_search
payment_logs = payment_logs.joins(:sourcing).where("sourcings.project_id = ?", project_id_search)
pick all payment_logs with its purchasing's project_id = project_id_search
payment_logs = payment_logs.(:purchasing).where("purchasings.project_id = ?", project_id_search)
We need to union 1 and 2 in order to pick up all the payment_logs whose project_id = project_id_search. What's the right way to accomplish it? We did not find union in rails and find_by_sql returns an array which is not what we want. Thanks.
payment_logs.where(["
payment_logs.sourcing_id IN (
SELECT id FROM sourcings WHERE sourcings.project_id = ?
)
OR payment_logs.purchasing_id IN
(
SELECT id FROM purchasings WHERE purchasings.project_id = ?
)", project_id_search, project_id_search])
Lot of SQL, but it should work
Option 2 (two SQL requests ...) :
payment_logs = []
payment_logs << PaymentLog.joins(:sourcing).where("sourcings.project_id" => project_id_search)
payment_logs << PaymentLog.joins(:purchasing).where("purchasings.project_id" => project_id_search)
payment_logs.uniq! #In case some records have both a sourcing and a purchasing
Option 3, with the squeel gem : https://github.com/ernie/squeel
PaymentLog.where{(source_id.in Sourcing.where(:project_id => project_id_search)) | (purchasing_id.in Purchasing.where(:project_id => project_id_search))}
I like this solution :)
Also, whenever you have a doubt on the generated SQL, from the console or anywhere else, you can add .to_sql at the end of an ActiveRecord query to double check the generated SQL

Rails - two queries combining them without SCOPES

Given two queries like:
#users1 = Users.find_by_company_id(2)
#users2 = Users.find_by_office_id(2)
I want to combine the two:
#users_to_show = #users1 + #users2
Issue is how how to prevent duplicate users from showing. Is there a way to combine the two (array?) And then ensure that duplicate records are removed?
Thanks
UPDATED:
# This QUERY gives all of a user's project members, people they work with
#project_ids = #projects.map(&:project_id)
#users = User.find_by_sql [
"SELECT DISTINCT users.*
FROM users
INNER JOIN permissions ON permissions.user_id = users.id
WHERE project_id IN (?) AND permissions.user_id != ?
UNION ALL
SELECT DISTINCT users.*
FROM users
WHERE instance_id = ?",
#project_ids, current_user.id, current_user.instance_id
]
This is a manual SQL approach that may work for you:
#users = User.find_by_sql("
SELECT DISTINCT * FROM USERS WHERE [Place your First complicated where clauses Here]
UNION
SELECT * FROM USERS WHERE instance_id = ?
", current_user.instance_id)
EDIT:
UNION will elimate duplicate records between the two queries.
EDIT: Be sure that each of the queries do not produce duplicates independently. Union will not remove duplicates inside single queries.

searching and sorting with paginate_by_sql -- will_paginate

I am using paginate_by_sql for a model's function.
def index
#refprobes = Refprobe.paginate_by_sql ["select * from ref_probe
where RPR_OID in
(SELECT DISTINCT RPR_OID
FROM REF_PROBE
JOIN ISH_PROBE ON RPR_OID = PRB_MAPROBE
JOIN ISH_SUBMISSION ON PRB_SUBMISSION_FK = SUB_OID
JOIN SEC_USER_PI ON USP_PI_ID = SUB_PI_FK
WHERE USP_USER_FK = " + session[:user_id]+ ")"], :page => params[:page], :per_page => 10
end
I want to add search and sorting to the above.
How do i do that?
Many many thanks
I realize this is quite old, but just came across it.
I guess you could always do something like this:
sql = <your existing sql>
order = " ORDER BY #{params[:order]}"
sql_to_paginate = sql + order
The other option would be to rewrite the query using Rails' syntax and then you could use the .reorder() method.
Hope this helps someone.

Resources