如何在rails3中使用AR查询接口指定多个值

时间:2011-02-17 22:15:33

标签: ruby-on-rails-3 activerecord

根据Active Record查询界面here上的rails指南2.2节:

这似乎表明我可以传递一个指定条件的字符串,然后是一个值数组,在构建arel时应该在某个时候替换它们。所以我有一个声明生成我的条件字符串,它可以是链接在一起的不同数量的属性与它们之间的AND或OR,我传入一个数组作为第二个arg到where方法,我得到:

  

ActiveRecord :: PreparedStatementInvalid:错误的绑定变量数(1表示5)

这让我相信我做错了。但是,我没有找到任何正确的方法。要以另一种方式重述问题,我需要将一个字符串传递给where方法,例如“table.attribute =?AND table.attribute1 =?OR table.attribute1 =?”将未知数量的这些条件一起加入或合并,然后传递一些东西,我认为是一个数组作为第二个参数,用于替换第一个参数条件字符串中的值。这是正确的做法,或者,我只是在某个地方错过了一些其他巨大的概念而且我对这一切都错了?我认为,不管怎么说,只要生成一个原始的sql字符串,就必须这样做。

7 个答案:

答案 0 :(得分:36)

这实际上非常简单:

Model.where(attribute: [value1,value2])

答案 1 :(得分:22)

听起来你正在做这样的事情:

Model.where("attribute = ? OR attribute2 = ?", [value, value])

你需要这样做:

# notice the lack of an array as the last argument
Model.where("attribute = ? OR attribute2 = ?", value, value)

请查看http://guides.rubyonrails.org/active_record_querying.html#array-conditions,详细了解其工作原理。

答案 2 :(得分:21)

而不是像这样多次将同一参数传递给where()

User.where(
  "first_name like ? or last_name like ? or city like ?", 
  "%#{search}%", "%#{search}%", "%#{search}%"
)

您可以轻松提供哈希

User.where(
  "first_name like :search or last_name like :search or city like :search",
  {search: "%#{search}%"}
)

使您的查询对于长参数列表更具可读性。

答案 3 :(得分:12)

  

听起来你正在做这样的事情:

Model.where("attribute = ? OR attribute2 = ?", [value, value])
     

你需要这样做:

#notice the lack of an array as the last argument
Model.where("attribute = ? OR attribute2 = ?", value, value) Have a
     看看   http://guides.rubyonrails.org/active_record_querying.html#array-conditions   有关其工作原理的详细信息。

非常接近。您可以使用* my_list将数组转换为参数列表。

Model.where("id = ? OR id = ?", *["1", "2"])

OR

params = ["1", "2"]
Model.where("id = ? OR id = ?", *params)

应该工作

答案 4 :(得分:4)

如果您想将开放式条件列表(属性名称和值)链接在一起,我建议使用arel表。

由于你的问题太模糊,所以提供具体细节有点困难,所以我只是解释如何为Post模型和一些属性的简单情况做这件事,比如titlesummaryuser_id(即用户has_many帖子)。

首先,获取模型的arel表:

table = Post.arel_table

然后,开始构建谓词(最终将用于创建SQL查询):

relation = table[:title].eq("Foo")
relation = relation.or(table[:summary].eq("A post about foo"))
relation = relation.and(table[:user_id].eq(5))

此处,table[:title]table[:summary]table[:user_id]posts表中列的表示形式。当你调用table[:title].eq("Foo")时,你正在创建一个谓词,大致相当于一个查找条件(获取标题列等于“Foo”的所有行)。这些谓词可以与andor链接在一起。

当您的聚合谓词准备就绪时,您可以使用以下结果获得结果:

Post.where(relation)

将生成SQL:

SELECT "posts".* FROM "posts"
WHERE (("posts"."title" = "Foo" OR "posts"."summary" = "A post about foo")
AND "posts"."user_id" = 5)

这将为您提供标题为“Foo”的所有帖子或属于ID为5的用户的摘要“关于foo的帖子”,

请注意,arel谓词可以无限链接在一起以创建越来越复杂的查询。这意味着如果你有(比方说)属性/值对的散列,以及知道是否在每个属性/值对上使用ANDOR的某种方式,你可以逐个循环遍历它们建立你的条件:

relation = table[:title].eq("Foo")
hash.each do |attr, value|
  relation = relation.and(table[attr].eq(value))
  # or relation = relation.or(table[attr].eq(value)) for an OR predicate
end
Post.where(relation)

除了链接条件的简易性之外,arel表的另一个优点是它们独立于数据库,因此您不必担心MySQL查询是否可以在PostgreSQL等中运行。

这是一个关于arel的更多Railscast:http://railscasts.com/episodes/215-advanced-queries-in-rails-3?view=asciicast

希望有所帮助。

答案 5 :(得分:2)

您可以使用哈希而不是字符串。使用你将要拥有的许多条件和相应的值构建一个哈希,并将其放入where方法的第一个参数中。

答案 6 :(得分:1)

<强> WRONG 这就是我过去常常做的事情。

keys = params[:search].split(',').map!(&:downcase)
# keys are now ['brooklyn', 'queens']

query = 'lower(city) LIKE ?'

if keys.size > 1
  # I need something like this depending on number of keys 
  # 'lower(city) LIKE ? OR lower(city) LIKE ? OR lower(city) LIKE ?'
 query_array = []
 keys.size.times { query_array << query }
 #['lower(city) LIKE ?','lower(city) LIKE ?']
 query = query_array.join(' OR ')
 # which gives me 'lower(city) LIKE ? OR lower(city) LIKE ?'
end
# now I can query my model
# if keys size is one then keys are just 'brooklyn', 
# in this case it is  'brooklyn', 'queens'
# @posts = Post.where('lower(city) LIKE ? OR lower(city) LIKE ?','brooklyn', 'queens' )
@posts = Post.where(query, *keys )
然而,

现在 - 是的 - 这非常简单。如nfriend21所述

Model.where(attribute: [value1,value2])

做同样的事情