ActiveRecord或查询


180

如何在Rails 3 ActiveRecord中执行OR查询。我发现的所有示例都具有AND查询。

编辑:OR方法自Rails 5起可用。请参见ActiveRecord :: QueryMethods


7
学习SQL。ActiveRecord使工作变得容易,但是您仍然需要知道查询在做什么,否则您的项目可能无法扩展。我还认为我可以不必使用SQL就可以解决问题,对此我是非常错误的。
ryan0

1
@ ryan0,您说得很对。我们可能会使用精美的宝石和其他东西,但是我们应该知道这些宝石在内部做什么以及底层技术是什么,并且可能会在需要时在没有宝石帮助的情况下使用底层技术。性能。创建宝石时会考虑特定数量的用例,但是在某些情况下,我们项目中的一个用例可能会有所不同。
rubyprince 2014年


1
从Rails 5开始,恕我直言,公认的答案应该是Greg Olsen版本:Post.where(column: 'something').or(Post.where(other: 'else'))
PhilippClaßen17年

6
这个问题的标签为ruby-on-rails-3。为什么接受的答案仅与Rails 5有关?
iNulty

Answers:


109

使用ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

结果SQL:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))

感觉有些混乱,但至少我没有编写sql,这感觉很不对!我将不得不更多地使用Arel。
pho3nixf1re 2010年

54
我不敢相信续集要优雅得多
Macario

3
SQL没有错。可能出错的是我们如何构建SQL字符串,即SQL Injection。因此,在将用户输入提供给必须针对数据库运行的SQL查询之前,我们必须对其进行清理。这将由ORM来处理,而这些已经处理了我们容易错过的边缘情况。因此,始终建议使用ORM创建SQL查询。
rubyprince 2014年

5
SQL的另一个问题是它与数据库无关。例如,matches将为LIKEsqlite(默认情况下不区分大小写)和ILIKEpostgres(需要显式不区分大小写的运算符)生成。
amoebe 2015年

1
@ToniLeigh ActiveRecord在后台使用SQL。它只是用于编写SQL查询的语法,用于处理SQL清理并使您的查询与数据库无关。唯一的开销是Rails将语法转换为SQL查询的地方。只需几毫秒。当然,您应该编写性能最好的SQL查询,然后尝试将其转换为ActiveRecord语法。如果无法以ActiveRecord语法表示SQL,则应使用普通SQL。
rubyprince

208

如果要对一列的值使用OR运算符,则可以将一个数组传递给.whereActiveRecord将使用IN(value,other_value)

Model.where(:column => ["value", "other_value"]

输出:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

这应该OR在单列上达到等效


13
这是最干净的“轨道方式”答案,应该被接受
koonse

10
感谢@koonse,它不完全是“ OR”查询,但是在这种情况下它会产生相同的结果。
deadkarma 2013年


79
该解决方案不能替代OR,因为您不能以这种方式使用两个不同的列。
fotanus 2014年

151

在Rails 3中,应该是

Model.where("column = ? or other_column = ?", value, other_value)

这也包括原始sql,但是我不认为ActiveRecord中有一种方法可以执行OR操作。您的问题不是菜鸟问题。


40
即使有原始sql,对于我来说,该语句也比Arel清晰得多。甚至DRYer。
jibiel 2011年

6
如果需要链接,其他表联接的列名称可能相同,则应按以下方式使用它:Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince 2012年

1
如果查询为表加上别名,那将失败。那就是当槟榔闪耀的时候。
DGM 2014年

58

Rails / ActiveRecord的更新版本可能会原生支持此语法。它看起来类似于:

Foo.where(foo: 'bar').or.where(bar: 'bar')

如该拉取请求所述https://github.com/rails/rails/pull/9052

现在,只需坚持以下工作即可:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

更新:根据https://github.com/rails/rails/pull/16052,or功能将在Rails 5中可用

更新:功能已合并到Rails 5分支


2
or现在可以使用Rails 5,但不能以这种方式实现,因为它希望传递1个参数。它期望一个Arel对象。查看已接受的答案
El'Magnifico

2
or如果直接使用ActiveRecord中的方法,则可以使用该方法:但是如果将其用于随后被链接的作用域,则可能会超出预期。
Jeremy List

@JeremyList所说的就是事实。那让我很难受。
courtsimas


23

Rails 5附带了一种or方法。(将墨水记入文档

此方法接受一个ActiveRecord::Relation对象。例如:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))

14

如果要使用数组作为参数,则以下代码可在Rails 4中使用:

query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms)  SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))

更多信息:在Rails 4中使用数组作为参数进行OR查询


9
或这样:Order.where(query.where_values.inject(:or))一路使用Arel。
卡梅隆·马丁

>在Rails 4中使用数组作为参数进行OR查询。有用的链接!谢谢!
Zeke Fast

7

MetaWhere插件完全是惊人的。

轻松混合OR和AND,在任何关联上加入条件,甚至指定OUTER JOIN!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}


3

我只是从客户端工作中提取了此插件,使您可以将合并范围与.or.,例如。Post.published.or.authored_by(current_user)。Squeel(MetaSearch的新实现)也很棒,但是不允许您使用OR范围,因此查询逻辑可能有点多余。


3

使用rails + arel,一个更清晰的方法:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

产生:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1

3

您可以这样做:

Person.where("name = ? OR age = ?", 'Pearl', 24)

或更优雅,请安装rails_or gem并执行以下操作:

Person.where(:name => 'Pearl').or(:age => 24)

-2

我想补充一下,这是一种搜索ActiveRecord的多个属性的解决方案。以来

.where(A: param[:A], B: param[:B])

将搜索A和B。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.