简体   繁体   中英

Pass array in raw MySQL query in Ruby on Rails

So, I have a problem. I have a query which returns ids from one table (say table1) and I have to pass those ids to another query which uses table2. (Writing inner selects or joins is not an option due to some certain reasons).

Query:

client = Mysql2::Client.new(:host => "localhost", :username => "", :password => "", :database =>"test")
query1 = %Q{select id from table1 where code='ABC123'}
ids = client.query(query1)
query2 = %Q{select * from table2 where `table2`.`table1_id` IN (#{ids}) and status="rejected"}
table2_data = client.query(query2)

ids is Mysql2::Result type Also, when I do ids.to_a, the resulting array has data something like this: [{"id"=>1}, {"id"=>2}] I need some feasible way to pass ids to the second query. I tried ids.to_a, but it gives error due to the braces [ ]. I have also tried concatenating, say the MySQL result is:

array = ids.to_a  # [1,2,3]
id_new = "("+#{array.join(',')}+")"

id_new becomes "(1,2,3)" which is a string and hence IN doesn't work.

Can anyone please suggest something how to pass ids array in the raw MySQL query? I have banged my head finding the answer, but couldn't find an appropriate one.

Edit : I can use Active Record only for query1 and if that is the case and ids is an Active Record object, can anyone suggest how to pass it to query2 in the IN clause which is supposed to be a raw SQL query?

Edit2 : I can't use Active Record (for query2) or join because it's making the query heavy and taking long time (>10s) to fetch the result (indices are present). So, I am using raw query to optimise it.

Are you sure it doesn't work because it is a string. I think it doesn't work because of duplicate brackets. Please try this:

array = ids.flat_map(&:values).join(',')
query2 = %Q{select * from table2 where `table2`.`table1_id` IN (#{array}) and status="rejected"}

I suggest to use a ORM (object-relational mapping) like the ActiveRecord or Sequel gems - especially because building database queries manually by string concatination is error prone and leads to vulnerabilities like sql injections.

When I ran similar queries to try to mimic your problem I saw that I'm getting an array of array for ids , like [["1"], ["2"], ["3"]] .

If this is also what you're getting then you should call ids.flatten before calling join :

query2 = %Q{select * from table2 where `table2`.`table1_id` IN (#{ids.flatten.join(',')}) and status="rejected"}

array.flatten removes extra braces, so:

[[1], [2], [3]].flatten
# => [1,2,3]

[[1], [2], [3]].flatten.join(',')
# => "1,2,3"

EDIT

Since you reported you are receiving a Mysql2::Result object, do this: ids.to_a.map(&:values).flatten.join(',')

The to_a first converts the Mysql2::Result to an array of hashes that looks like this:

[{"id"=>"1"}, {"id"=>"2"}]

Then using map(&:values) we convert it to an array that looks like this:

[["1"], ["2"]]

This array is similar to the above (before the edit), so running flatten.join(',') converts it to the string you are looking for.

Note that instead of doing map(&:values).flatten you could use the common shortcut flat_map(&:values) which results in the same thing.

If the main reason you posted was to learn how to extract data from an array of hashes, then you can ignore this answer.

However, if you wanted the best way to get the data from the database, I'd suggest you use ActiveRecord to do the donkey work for you:

class Table1 < ActiveRecord::Base
  self.table_name = :table1
  has_many :table2s
end

class Table2 < ActiveRecord::Base
  self.table_name = :table2
  belongs_to :table1
end

table2_data = Table2.joins(:table1).where(table1: {code: 'ABC123'}, status: 'rejected') 

A key point is that a SQL join , will effectively do the processing of the IDs for you. You could code up the SQL join yourself, but ActiveRecord will do that for you, and allow you to add the additional queries, such that you can gather the data you want in one query.

You can join array with comma , like following code.

ids = ids.to_a.map{|h| h['id']}
query2 = %Q{select * from table2 where `table2`.`table1_id` IN (#{ids.join(',')}) and status="rejected"}  
table2_data = client.query(query2)

It will work fine.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM