让我们说我有一个数组(从客户端收到)的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
表达式的预准备语句语法的正确方法是什么。
答案 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)