Rails:使用大于/小于where语句


142

我正在尝试查找ID大于200的所有用户,但是在使用特定语法时遇到了一些麻烦。

User.where(:id > 200) 

User.where("? > 200", :id) 

都失败了。

有什么建议?

Answers:



124

我仅在Rails 4中对此进行了测试,但是有一种有趣的方式将范围与where哈希一起使用来获得此行为。

User.where(id: 201..Float::INFINITY)

将生成SQL

SELECT `users`.* FROM `users`  WHERE (`users`.`id` >= 201)

可以用少于做相同的事情-Float::INFINITY

我刚刚在SO上发布了一个类似的问题,询问是否要使用日期。

>=>

为避免人们不得不深入研究和关注评论对话,这里是重点。

上面的方法仅生成>=查询,而不生成>。有很多方法可以处理此替代方案。

对于离散数

您可以使用上述number_you_want + 1策略,让我对“用户”感兴趣,id > 200但实际上却在寻找id >= 201。这对于整数和数字都可以,您可以按一个感兴趣的单位递增。

如果您将数字提取到一个命名良好的常量中,则一目了然可能是最容易阅读和理解的。

倒逻辑

我们可以使用事实,x > y == !(x <= y)并使用where not链。

User.where.not(id: -Float::INFINITY..200)

生成SQL

SELECT `users`.* FROM `users` WHERE (NOT (`users`.`id` <= 200))

这需要花额外的一秒钟时间来阅读并进行推理,但适用于不能使用 + 1策略的。

Arel表

如果想花哨的话,可以利用Arel::Table

User.where(User.arel_table[:id].gt(200))

将生成SQL

"SELECT `users`.* FROM `users` WHERE (`users`.`id` > 200)"

具体如下:

User.arel_table              #=> an Arel::Table instance for the User model / users table
User.arel_table[:id]         #=> an Arel::Attributes::Attribute for the id column
User.arel_table[:id].gt(200) #=> an Arel::Nodes::GreaterThan which can be passed to `where`

这种方法将为您提供您感兴趣的确切 SQL,但是没有多少人直接使用Arel表,并且会发现它凌乱和/或令人困惑。您和您的团队将知道最适合您的。

奖金

从Rails 5开始,您还可以添加日期!

User.where(created_at: 3.days.ago..DateTime::Infinity.new)

将生成SQL

SELECT `users`.* FROM `users` WHERE (`users`.`created_at` >= '2018-07-07 17:00:51')

双倍奖金

Ruby 2.6发布后(2018年12月25日),您将可以使用新的无限范围语法!取而代之的是201..Float::INFINITY你只能写东西201..。更多信息,请参阅此博客文章


3
出于好奇,为什么这要优于公认的答案?
mecampbellsoup

5
上级有误导性。通常,如果您能够对字符串使用哈希语法,则可以通过ARel查询获得更大的灵活性,这就是为什么许多人更喜欢此解决方案的原因。根据您的项目/团队/组织的不同,您可能希望某些人可以更轻松地看一眼代码以找出答案,即可接受的答案。
亚伦

2
我不相信您可以使用基本的where匹配器来做到这一点。为了简单起见,>我建议使用a >= (number_you_want + 1)。如果您真的想确保它只是一个>查询,则可以访问ARel表。每个继承自的类ActiveRecord都有一个arel_tablegetter方法,该方法返回Arel::Table该类的。表格中的列可使用[]方式访问User.arel_table[:id]。这将返回一个Arel::Attributes::Attribute您可以调用gt并传递的信息200。然后可以将其传递给where。例如User.where(User.arel_table[:id].gt(200))
亚伦

2
@bluehallu您能提供一个例子吗?以下对我不起作用User.where(created_at: 3.days.ago..DateTime::Infinity.new)
亚伦

1
啊! Rails 5中的新变化很可能会给您带来有益的行为。在Rails 4.2.5(Ruby 2.2.2)中,您将获得带有的查询WHERE (users.created_at >= '2016-04-09 14:31:15' AND users.created_at < #<Date::Infinity:0x00>)(对于SO注释格式,省略了表和列名的反斜线)。
亚伦

22

更好的用法是在用户模型中创建范围 where(arel_table[:id].gt(id))


6

如果您想要更直观的写作,可以使用名为squeel的gem 来编写如下指令:

User.where{id > 200}

请注意,“括号”字符{} id只是文本。

您要做的就是将squeel添加到您的Gemfile中:

gem "squeel"

在Ruby中编写复杂的SQL语句时,这可能会大大减轻您的生活。


11
我建议避免使用squeel。长期难以维持,有时行为异常。某些Active Record版本也有问题
John Owen Chile

我使用squeel已有几年了,但仍然对此感到满意。但是也许值得尝试另一个ORM,例如续集(<> squeel),它似乎有望替代ActiveRecord成为一个不错的功能。
道格拉斯

5

阿瑞尔是你的朋友。

User.where(User.arel_table [:id] .gt(200))


4

另一种幻想的可能性是...

User.where("id > :id", id: 100)

如果要在多个位置进行替换,此功能使您可以创建更易于理解的查询,例如...

User.where("id > :id OR number > :number AND employee_id = :employee", id: 100, number: 102, employee: 1205)

这比?在查询中包含大量内容更具意义。

User.where("id > ? OR number > ? AND employee_id = ?", 100, 102, 1205)

3

我经常在日期字段(比较运算符很常见)中遇到这个问题。

为了进一步阐述Mihai的答案,我认为这是一个可靠的方法。

您可以向模型添加如下范围:

scope :updated_at_less_than, -> (date_param) { 
  where(arel_table[:updated_at].lt(date_param)) }

...然后在您的控制器中,或者在您使用模型的任何地方:

result = MyModel.updated_at_less_than('01/01/2017')

...带有连接的更复杂的示例如下所示:

result = MyParentModel.joins(:my_model).
  merge(MyModel.updated_at_less_than('01/01/2017'))

这种方法的一个巨大优势是(a)它允许您从不同的范围组合查询,并且(b)当您两次连接到同一表时避免别名冲突,因为arel_table将处理查询生成的那部分。


1

Rails 6.1+

Rails 6.1在where条件下为比较运算符添加了新的“语法” ,例如:

Post.where('id >': 9)
Post.where('id >=': 9)
Post.where('id <': 3)
Post.where('id <=': 3)

因此,您的查询可以重写如下:

User.where('id >': 200) 

这是PR的链接,您可以在其中找到更多示例。


-3

更短:

User.where("id > 200")

8
我认为这必须是动态的,并且发布者希望通过使用参数化查询(where("id > ?", 200)语法)来避免SQL注入。这没有实现。
马修·希尼亚
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.