问题 原始数据库在Rails中查询


我试图在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


2321
2018-01-28 18:06


起源

什么是错误? - Nicolas Garnil
刚刚更新了它。 - gmile
因为我经常查询MySQL,所以我不确定postgresql。但无论如何,语法应该是相同的。尝试: query = 'SELECT * FROM users WHERE id IN (?);'  results = ActiveRecord::Base.connection.exec_query(query, [1,2]) - Utsav Kesharwani


答案:


我很确定你的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] ]
)

11
2018-01-28 19:09



似乎也适用于sqlite3。 - Yorick Sijsling


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


2
2018-04-21 21:47



我也碰到了这个问题,但通过这样做解决了这个问题: 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])) - saywhatnow


我不清楚你为什么不在其中创建带插值的查询字符串:

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)

-1
2018-03-30 00:56



这具有必须消毒价值的缺点。它还意味着您不能在预准备语句中使用相同的查询字符串 - Oren S
同意。但OP没有将这些限制为约束。 - aenw