Rails 中的原始数据库查询

Posted

技术标签:

【中文标题】Rails 中的原始数据库查询【英文标题】:Raw DB querying in Rails 【发布时间】:2014-02-20 05:04:11 【问题描述】:

我正在尝试在 rails 中运行以下原始查询,但发现它失败了:

query   = 'SELECT * FROM users WHERE id IN ($1);'
results = ActiveRecord::Base.connection.exec_query(query, "My query", [ [1,2] ]);

我做错了什么?

我遇到的错误是这样开始的:

Could not log "sql.active_record" event. NoMethodError: undefined method `binary?' for 1:Fixnum

显然,我以某种方式滥用了[1, 2] 绑定参数,但我自己找不到合适的示例。

附:这是最小的失败示例,它源自一个更高级的查询,它不能转换为 ActiveRecord 调用链。换句话说,我在构建查询时不能依赖 Arel。

P.P.S.我正在使用rails 4.0.1postgresql 9.3

【问题讨论】:

我不确定 postgresql,因为我通常查询 mysql。但无论如何,语法应该是一样的。试试看:query = 'SELECT * FROM users WHERE id IN (?);'results = ActiveRecord::Base.connection.exec_query(query, [1,2]) 【参考方案1】:

我很确定您的 NoMethodError 来自日志记录。如果我们查看exec_query,我们会看到:

def exec_query(sql, name = 'SQL', binds = [])
  log(sql, name, binds) do
    # call exec_no_cache(sql, binds) or exec_cache(sql, binds)...

如果我们查看exec_cache,我们会看到:

def exec_cache(sql, binds)
  #..
  @connection.send_query_prepared(stmt_key, binds.map  |col, val|
    type_cast(val, col)
  )

所以binds 应该是列/值对。 PostgreSQL 驱动程序期望col 是一个列对象,以便它可以询问它的名称是什么以及它应该如何格式化val,该信息被exec_query 中的log 调用使用以产生漂亮的东西在 Rails 日志中可读。一些实验表明您可以将nil 用作col,一切都很顺利。

这意味着我们已经开始这样做了:

exec_query(
  'SELECT * FROM users WHERE id IN ($1)',
  'my query',
  [ [nil, [1,2]] ]
)

底层驱动程序可能知道也可能不知道如何处理[1,2] 数组,我只有Rails3 和PostgreSQL 扩展可供测试,它不喜欢[1,2]。如果 Rails4 也不喜欢这个数组,那么你可以一个接一个地传递参数:

exec_query(
  'SELECT * FROM users WHERE id IN ($1, $2)',
  'my query',
  [ [nil,1], [nil,2] ]
)

【讨论】:

【参考方案2】:

我最近遇到了类似的问题。事实证明,在where in (?) 中,ActiveRecord 需要一个字符串,而不是一个数组。因此,您可以尝试传入一串以逗号分隔的 id,这应该可以解决问题。

【讨论】:

我也遇到了这个问题,但通过这样做解决了它:sql = "SELECT * FROM table_a WHERE identifier IN (?)"where_equals = ["id_1", "id_2"]ActiveRecord::Base.connection.exec_query(ActiveRecord::Base.send(:sanitize_sql_array, [sql, where_equals])) (?) 语法起初对我不起作用。我切换到使用备用 '%s' 语法,然后最终使用哈希语法。 e.g. sanitize_sql_array(["name=:name and group_id=:group_id", name: "foo'bar", group_id: 4]) # => "name='foo''bar' and group_id=4" sanitize_sql_array(["name='%s' and group_id='%s'", "foo'bar", 4]) # => "name='foo''bar' and group_id='4'" 来自api.rubyonrails.org/classes/ActiveRecord/Sanitization/…

以上是关于Rails 中的原始数据库查询的主要内容,如果未能解决你的问题,请参考以下文章

Rails 对 csv 格式的原始查询,通过控制器返回

如何从 Ruby on Rails 中的数据库查询中消除 N+1 查询?

修改 rails mongoid 查询中的值

Rails MySQL 查询时间混乱

Rails 4,返回查询数据和总数

Rails 3.0 中的 Arel 到底是啥?