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

Ada*_*ton 124 syntax ruby-on-rails where

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

User.where(:id > 200) 
Run Code Online (Sandbox Code Playgroud)

User.where("? > 200", :id) 
Run Code Online (Sandbox Code Playgroud)

都失败了.

有什么建议?

小智 248

试试这个

User.where("id > ?", 200) 
Run Code Online (Sandbox Code Playgroud)

  • 它自动转义200(用户可以输入值,它避免了SQL注入攻击的可能性) (18认同)
  • 有没有理由更喜欢使用`?`,而不是内联`200`? (7认同)

Aar*_*ron 109

我只在Rails 4中对此进行了测试,但有一种有趣的方法可以使用带where散列的范围来获得此行为.

User.where(id: 201..Float::INFINITY)
Run Code Online (Sandbox Code Playgroud)

将生成SQL

SELECT `users`.* FROM `users`  WHERE (`users`.`id` >= 201)
Run Code Online (Sandbox Code Playgroud)

同样可以做到少于-Float::INFINITY.

我刚刚发布了一个类似的问题,询问有关在SO上使用日期这样做的事情.

>= VS >

为避免人们不得不深入挖掘并遵循评论对话,这里是重点.

上面的方法只生成一个>=查询,而不是一个>.有很多方法可以处理这种替代方案.

对于离散数字

您可以使用number_you_want + 1上面的策略,我对用户感兴趣id > 200但实际上正在寻找id >= 201.这适用于整数和数字,您可以通过单个兴趣单位递增.

如果您将数字提取到一个名为常数的常数中,这可能是最容易阅读和理解的一目了然.

倒逻辑

我们可以使用这个事实x > y == !(x <= y)并使用where not chain.

User.where.not(id: -Float::INFINITY..200)
Run Code Online (Sandbox Code Playgroud)

它生成SQL

SELECT `users`.* FROM `users` WHERE (NOT (`users`.`id` <= 200))
Run Code Online (Sandbox Code Playgroud)

这需要额外的秒来阅读和推理,但是对于不能使用+ 1策略的非离散值或列,它们将起作用.

Arel表

如果你想得到幻想,你可以利用Arel::Table.

User.where(User.arel_table[:id].gt(200))
Run Code Online (Sandbox Code Playgroud)

将生成SQL

"SELECT `users`.* FROM `users` WHERE (`users`.`id` > 200)"
Run Code Online (Sandbox Code Playgroud)

具体如下:

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`
Run Code Online (Sandbox Code Playgroud)

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

奖金

从Rails 5开始,您也可以使用日期执行此操作!

User.where(created_at: 3.days.ago..DateTime::Infinity.new)
Run Code Online (Sandbox Code Playgroud)

将生成SQL

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

双倍奖金

一旦Ruby 2.6发布(2018年12月25日),您将能够使用新的无限范围语法!而不是201..Float::INFINITY你能够写201...此博客文章中的更多信息.

  • 优越是误导.通常,如果您能够在字符串上使用哈希语法,那么您可以使用ARel查询获得更大的灵活性,这就是为什么许多人更喜欢这种解决方案.根据您的项目/团队/组织,您可能希望某些人更容易看到代码来弄清楚,这是接受的答案. (4认同)
  • 出于好奇,为什么这优于公认的答案? (3认同)
  • 这个答案最适合 Rails 4 IMO。已经使用它一段时间了,效果很好。 (2认同)
  • 我不相信你可以使用基本的`where`匹配器来做到这一点.对于`>`我建议使用`> =(number_you_want + 1)`以简化.如果你真的想确保它只是一个`>`查询,你可以访问ARel表.从`ActiveRecord`继承的每个类都有一个`arel_table` getter方法,它返回该类的`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)`. (2认同)

Mih*_*hai 22

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


Mar*_*n13 17

更新

Rails 核心团队决定暂时恢复此更改,以便更详细地讨论它。有关更多信息,请参阅此评论此 PR

我仅出于教育目的而留下我的答案。


用于在 Rails 6.1 中进行比较的新“语法”(已恢复)

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

Post.where('id >': 9)
Post.where('id >=': 9)
Post.where('id <': 3)
Post.where('id <=': 3)
Run Code Online (Sandbox Code Playgroud)

所以你的查询可以改写如下:

User.where('id >': 200) 
Run Code Online (Sandbox Code Playgroud)

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


Dou*_*las 6

如果你想要一个更直观的写作,它存在一个名为squeel的宝石,可以让你像这样编写你的指令:

User.where{id > 200}
Run Code Online (Sandbox Code Playgroud)

注意'大括号'字符{}并且id只是一个文本.

您所要做的就是在Gemfile中添加squeel:

gem "squeel"
Run Code Online (Sandbox Code Playgroud)

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

  • 我建议避免使用吱吱声.长期难以维持,有时会有奇怪的行为.某些Active Record版本也存在问题 (10认同)

Vic*_*tor 6

另一种奇特的可能性是...

User.where("id > :id", id: 100)
Run Code Online (Sandbox Code Playgroud)

如果您想在多个位置替换,此功能允许您创建更易于理解的查询,例如...

User.where("id > :id OR number > :number AND employee_id = :employee", id: 100, number: 102, employee: 1205)
Run Code Online (Sandbox Code Playgroud)

这比?在查询上有很多更有意义......

User.where("id > ? OR number > ? AND employee_id = ?", 100, 102, 1205)
Run Code Online (Sandbox Code Playgroud)


joe*_*alt 5

阿瑞尔是你的朋友。

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