我需要将一个id数组传递到我的原始sql查询中,如下所示:
select offers.* from offers where id in (1,2,3,4,5)
真正的查询包含许多联接和聚合函数,不能使用Arel表达式或像Offer.where(id: [...])
这样的ActiveRecord模型方法编写。我正在寻找如何在原始查询中使用绑定变量。
我不想在字符串中插入id,而是想使用这样的绑定变量(伪代码):
ActiveRecord::Base.connection.select_all("select offers.* from offers where id in (:ids)", {ids: [1,2,3,4,5]})
然而,我找不到任何解决方案来执行此操作。从这张票上,我得到了一个关于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)
我尝试过这种方法,但它根本不起作用,我的"?"并没有被实际值所取代。
我使用的是Rails 5.1.6和MariaDB数据库。
纯使用arel
可以以简单得多的方式完成此操作。(它还使代码比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)
这将导致以下SQL
SELECT
[offers].*
FROM
[offers]
WHERE
[offers].[id] IN (1,2,3,4,5)
执行时,您将收到一个ActiveRecord::Result
对象,该对象通常最容易通过调用to_hash
来处理,生成的每一行都将变成{column_name => value}
的Hash
但是,如果您使用的是rails,并且Offer
是一个真正的型号,那么:
Offer.where(id: ids)
将导致相同的查询,并将返回Offer
对象的ActiveRecord::Relation
集合,这通常更可取。
更新
似乎需要在mysql2中启用prepared_statements
(mariadb
)才能使用绑定参数,可以这样做:
default: &default
adapter: mysql2
encoding: utf8
prepared_statements: true # <- here we go!
请注意以下代码: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/abstract_adapter.rb#L630
https://github.com/rails/rails/blob/5-1-stable/activerecord/lib/active_record/connection_adapters/mysql/database_statements.rb#L30
正如您在上一个代码中看到的,如果prepared_statements
关闭(这似乎是mysql2
适配器的默认设置),exec_query
将忽略bind_params
。