我试图在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]
以某种方式绑定params,但我自己找不到合适的例子。
附:这是最小的失败示例,派生自更高级的查询,无法转换为ActiveRecord调用链。换句话说 - 在构建查询时我不能依赖Arel。
P.P.S.我在用着 rails 4.0.1
和 postgresql 9.3
我很确定你的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
,该信息被使用 log
打电话给 exec_query
在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] ]
)
我最近遇到了类似的问题。事实证明,在 where in (?)
,ActiveRecord期待一个字符串,而不是一个数组。所以你可以尝试传入一串逗号分隔的ID,这应该可以解决问题。
我不清楚你为什么不在其中创建带插值的查询字符串:
id_list = [1,3,5,7,9,13]
# use .join to create a string from the id_list using a comma inbetween each one
query = "SELECT * FROM users WHERE id IN (#{id_list.join(', ')})"
# query = "SELECT * FROM users WHERE id IN (1, 3, 5, 7, 9, 13)"
results = ActiveRecord::Base.connection.exec_query(query)