【问题标题】:execute_query with array as bind parameter以数组作为绑定参数的execute_query
【发布时间】:2021-09-07 19:07:26
【问题描述】:

如何在select_allexec_query 中使用数组(字符串或整数)作为in where 子句的绑定参数?

# imagine a long a complicated SQL statement
# containing many CTEs and  
sql = <<~SQL
with things as (
  select ....
  where 
    x in ($1)
), morethings as (
  select
)

select 
sum(...), 
avg(...)
from morethings 
where categories in ($2)
SQL

binds = ??
ActiveRecord::Base.connection.exec_query(
  sql,
  "query name",
   binds
).to_a

我可以像这样为标量生成绑定:

  ActiveRecord::Relation::QueryAttribute.new("name", "Pascal", ActiveRecord::Type::String.new)

但不知道如何提供数组值,我似乎遗漏了一些东西。

背景:我想在 Rails 项目中运行一些原始 SQL 查询,并正确转义参数。 DB 是 Postgresql,但我认为有一种与 DB 无关的方式? 还是我必须使用原始连接。

查询与模型无关,可以转换为 Arel,但并不真正适合工作流程。

【问题讨论】:

  • 你有没有想过看一下splat运算符freecodecamp.org/news/…?还是使用命名绑定(允许您使用命名哈希)?
  • @jad 我看不出 splats 或命名绑定如何解决问题。问题是传递一个 as 参数。
  • 正如这篇文章中的答案所暗示的,您可能值得使用 Arel 来解决您的问题。如果您真的想按照您所说的方式执行此操作,并且想要将参数列表(而不是我还建议的一组命名参数)传递给 exec_query,您应该使用 splat 传递它们以便 exec 查询可以处理它们...但是如果您正在编写纯 SQL 代码并使用 Ruby 发送它,那么您错误地使用了 Ruby
  • 数组中的元素个数事先是未知的。位置参数已编号。与泼溅恕我直言无关。如果我想执行 SQL 查询,我也不知道为什么我会错误地使用 Ruby。
  • Ruby on Rails 拥有整个对象的功能。如果您使用 Ruby,最好定义模型,并使用模型之间的关系到达您想要的位置。它提高了调试能力,一旦你开始理解它,它就更容易编码和理解。运行纯 SQL 然后希望使用结果是不可移植的,并且是调试的噩梦,尤其是在与其他程序员一起工作时

标签: ruby-on-rails postgresql activerecord


【解决方案1】:
query = Arel::Table.new(:users).then do |u|
  users.project(users[Arel.star])
       .where(users[:id].in([1, 2, 3, 'a', 'b', 'c']))
end

query.to_sql
# => "SELECT \"users\".* FROM \"users\" WHERE \"users\".\"id\" IN (1, 2, 3, 'a', 'b', 'c')"

【讨论】:

  • 但老实说,如果你有一个模型,为什么不直接用它来生成查询呢?
  • 你好@max 感谢您的回答。我希望在没有 Arel 的情况下过得去 :-) 我没有这个特定查询的模型,我会更新问题以更好地显示这一点。这是一个涉及 CTE 的复杂查询。
【解决方案2】:

如果您愿意进行一些字符串操作预处理,则可以安全地执行此操作。我们构建了一个字符串,一旦它到达数据库,它就可以转换为类型化的 postgres 数组。

user_ids = [1,3,5,34]
user_id_pg_array = '{' + user_ids.join(',') + '}' # evaluates to '{1,3,5,34}'
ActiveRecord::Base.connection.exec_query(
  "SELECT status FROM notifications WHERE user_id = ANY($1::int[]),
  "my fav query",
  [ActiveRecord::Relation::QueryAttribute.new("user_id_pg_arr", user_id_pg_arr, ActiveRecord::Type::String.new)]
)

我不是 Ruby 或 Rails 专家,所以可能有更好的方法。我希望 db 客户端库不假思索地为我处理这个问题,所以如果你下拉到特定于 postgres 的 gem,也许有办法做到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多