简体   繁体   English

如何将数组作为绑定变量传递给Rails / ActiveRecord原始SQL查询?

[英]How to pass array as bind variable to Rails/ActiveRecord raw SQL queries?

I need to pass an array of ids into my raw sql query like this: 我需要像这样将ID数组传递到我的原始sql查询中:

select offers.* from offers where id in (1,2,3,4,5)

The real query includes a lot of joins and aggregation functions and can't be written using Arel expressions or ActiveRecord model methods like Offer.where(id: [...]) . 真正的查询包含许多联接和聚合函数,不能使用Arel表达式或ActiveRecord模型方法(如Offer.where(id: [...]) I'm looking exactly for how to use bind variables in raw queries. 我正在寻找如何在原始查询中使用绑定变量。

Instead of interpolating ids into string I want to use bind variables like this (pseudo-code): 与其将id内插到字符串中,不如使用绑定变量(伪代码):

ActiveRecord::Base.connection.select_all("select offers.* from offers where id in (:ids)", {ids: [1,2,3,4,5]})

However, I can't find any solution to perform this. 但是,我找不到任何解决方案来执行此操作。 From this ticket I've got a comment with related test-case in ActiveRecord code with the following example: 这张票证中,我得到了ActiveRecord代码中相关测试用例的注释,并带有以下示例:

sub   = Arel::Nodes::BindParam.new
binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)]
sql   = "select * from topics where id = #{sub.to_sql}"

@connection.exec_query(sql, "SQL", binds)

I've tried this approach, but it didn't worked at all, my "?" 我已经尝试过这种方法,但是根本没有用,我的“?” was not replaced by actual values. 没有被实际值代替。

I'm using Rails 5.1.6 and MariaDB database. 我正在使用Rails 5.1.6和MariaDB数据库。

You could do this in a much simpler fashion purely with arel . 您完全可以使用arel来以更简单的方式执行此arel (Also it makes the code far more maintainable than SQL strings) (这也使代码比SQL字符串更具可维护性)

offers = Arel::Table.new('offers') 
ids = [1,2,3,4,5]
query = offers.project(Arel.star).where(offers[:id].in(ids))
ActiveRecord::Base.connection.exec_query(query.to_sql)

This will result in the following SQL 这将导致以下SQL

SELECT 
  [offers].*
FROM 
  [offers]
WHERE 
  [offers].[id] IN (1,2,3,4,5)

When executed you will receive an ActiveRecord::Result object with is usually easiest to deal with by calling to_hash and each resulting row will be turned into a Hash of {column_name => value} 执行后,您将收到一个ActiveRecord::Result对象,通常最容易通过调用to_hash进行处理,并且每个结果行都将被转换为{column_name => value}Hash

However if you are using rails and Offer is a true model then: 但是,如果您使用的是Rails,并且Offer是真实的模型,则:

Offer.where(id: ids)

Will result in the same query and will return an ActiveRecord::Relation collection of Offer objects which is generally more preferable. 将导致相同的查询,并将返回Offer对象的ActiveRecord::Relation集合,通常更可取。

Update 更新

Seems like you need to enable prepared_statements in mysql2 ( mariadb ) in order to use the bind params, which can be done like this: 好像你需要启用prepared_statements在mysql2( mariadb ,以使用绑定参数,可以它可以这样做):

default: &default
  adapter: mysql2
  encoding: utf8
  prepared_statements: true  # <- here we go!

Please note the following pieces of code: https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L115 请注意以下代码: https : //github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L115

https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L40 https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb#L40

https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L630 https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L630

https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb#L30 https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb#L30

As you can see in the last code exec_query ignores bind_params if prepared_statements is turned off (which appears to be the default for the mysql2 adapter). 正如你可以在最后的代码中看到exec_query忽略bind_params如果prepared_statements被关闭(这似乎是在默认mysql2适配器)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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