使用WHERE IN和Rails预处理语句语法

时间:2013-09-29 08:30:09

标签: sql ruby ruby-on-rails-3 prepared-statement rails-activerecord

让我们说我有一个数组(从客户端收到)的ids:

myArray = [1,5,19,27]

我想返回(次要)ID在该列表中的所有项目。

在SQL中,这将是:

SELECT * FROM Items WHERE id IN (1,5,19,27)

我知道我能做到:

Item.where(id: [1,5,9,27])

然而,查询此问题的时间越长,使用预准备语句语法Item.where('myAttrib = ? AND myOtherAttrib <> ? AND myThirdAttrib = ?', myVal[0], myVa[1], myVal[2])

考虑到这一点,我想要的是以下内容:

Item.where('id IN ?', myArray)

但是,这会产生语法错误:

ActiveRecord::StatementInvalid: PG::Error: ERROR: syntax error at or near "1" LINE 1: SELECT "items".* FROM "items" WHERE (id in 1,2,3,4)

我该如何解决这个问题?使用IN表达式的预准备语句语法的正确方法是什么。

3 个答案:

答案 0 :(得分:11)

我最终使用了:

Item.where('id IN (?)', myArray)

答案 1 :(得分:4)

您需要将查询更改为以下内容:

Item.where('id = ?', myArray)

如果myArray实际上是一个数组,那么ActiveRecord会将其转换为IN子句。

答案 2 :(得分:0)

使用原始sql的问题是你丢失了Rails和AREL免费提供的列名的自动命名空间。

在组合和合并查询或范围时,或者在进行连接时,使用原始sql可能会引发问题。

无需使用原始sql来使用IN

Item.where(:id => myArray) # SELECT * FROM items WHERE id IN (...)

myArray可以是值数组或Items数组,也可以是单个值。

Rails非常聪明,可以将SQL更改为您的意思。事实上,如果数组包含nil,则SQL将为SELECT * FROM items WHERE (id IS NULL or id IN (...))。这明显优于使用静态SQL并且只能处理单个用例的原始SQL。

如果Rails没有为您提供所需的信息,您可以回到AREL。例如:

class Item
  scope :with_category, ->(categories) {
    where( arel_table[:category].in(categories) )
  }

  scope :filter, ->(a, b, c) {
    not_b = arel_table[:myOtherAttrib].not_eq(b)
    where(:myAttrib => a, :myThirdAttrib => c).where(not_b)
  }
end

Item.with_category(['small', 'large']).filter(*myVal)