[ruby-on-rails] Rails: Using greater than/less than with a where statement

I'm trying to find all Users with an id greater than 200, but I'm having some trouble with the specific syntax.

User.where(:id > 200) 

and

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

have both failed.

Any suggestions?

This question is related to ruby-on-rails syntax where

The answer is


Shorter:

User.where("id > 200")

Another fancy possibility is...

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

This feature allows you to create more comprehensible queries if you want to replace in multiple places, for example...

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

This has more meaning than having a lot of ? on the query...

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

Arel is your friend:

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

I've only tested this in Rails 4 but there's an interesting way to use a range with a where hash to get this behavior.

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

will generate the SQL

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

The same can be done for less than with -Float::INFINITY.

I just posted a similar question asking about doing this with dates here on SO.

>= vs >

To avoid people having to dig through and follow the comments conversation here are the highlights.

The method above only generates a >= query and not a >. There are many ways to handle this alternative.

For discrete numbers

You can use a number_you_want + 1 strategy like above where I'm interested in Users with id > 200 but actually look for id >= 201. This is fine for integers and numbers where you can increment by a single unit of interest.

If you have the number extracted into a well named constant this may be the easiest to read and understand at a glance.

Inverted logic

We can use the fact that x > y == !(x <= y) and use the where not chain.

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

which generates the SQL

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

This takes an extra second to read and reason about but will work for non discrete values or columns where you can't use the + 1 strategy.

Arel table

If you want to get fancy you can make use of the Arel::Table.

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

will generate the SQL

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

The specifics are as follows:

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`

This approach will get you the exact SQL you're interested in however not many people use the Arel table directly and can find it messy and/or confusing. You and your team will know what's best for you.

Bonus

Starting in Rails 5 you can also do this with dates!

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

will generate the SQL

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

Double Bonus

Once Ruby 2.6 is released (December 25, 2018) you'll be able to use the new infinite range syntax! Instead of 201..Float::INFINITY you'll be able to just write 201... More info in this blog post.


I often have this problem with date fields (where comparison operators are very common).

To elaborate further on Mihai's answer, which I believe is a solid approach.

To the models you can add scopes like this:

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

... and then in your controller, or wherever you are using your model:

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

... a more complex example with joins looks like this:

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

A huge advantage of this approach is (a) it lets you compose your queries from different scopes and (b) avoids alias collisions when you join to the same table twice since arel_table will handle that part of the query generation.


If you want a more intuitive writing, it exist a gem called squeel that will let you write your instruction like this:

User.where{id > 200}

Notice the 'brace' characters { } and id being just a text.

All you have to do is to add squeel to your Gemfile:

gem "squeel"

This might ease your life a lot when writing complex SQL statement in Ruby.


A better usage is to create a scope in the user model where(arel_table[:id].gt(id))


Update

Rails core team decided to revert this change for a while, in order to discuss it in more detail. See this comment and this PR for more info.

I am leaving my answer only for educational purposes.


new 'syntax' for comparison in Rails 6.1 (Reverted)

Rails 6.1 added a new 'syntax' for comparison operators in where conditions, for example:

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

So your query can be rewritten as follows:

User.where('id >': 200) 

Here is a link to PR where you can find more examples.


Examples related to ruby-on-rails

Embed ruby within URL : Middleman Blog Titlecase all entries into a form_for text field Where do I put a single filter that filters methods in two controllers in Rails Empty brackets '[]' appearing when using .where How to integrate Dart into a Rails app Rails 2.3.4 Persisting Model on Validation Failure How to fix "Your Ruby version is 2.3.0, but your Gemfile specified 2.2.5" while server starting Is the server running on host "localhost" (::1) and accepting TCP/IP connections on port 5432? Rails: Can't verify CSRF token authenticity when making a POST request Uncaught ReferenceError: React is not defined

Examples related to syntax

What is the 'open' keyword in Swift? Check if returned value is not null and if so assign it, in one line, with one method call Inline for loop What does %>% function mean in R? R - " missing value where TRUE/FALSE needed " Printing variables in Python 3.4 How to replace multiple patterns at once with sed? What's the meaning of "=>" (an arrow formed from equals & greater than) in JavaScript? How can I fix MySQL error #1064? What do >> and << mean in Python?

Examples related to where

Get only records created today in laravel Can I do Model->where('id', ARRAY) multiple where conditions? SQLSTATE[42S22]: Column not found: 1054 Unknown column - Laravel SQL Query with Join, Count and Where PHP MySQL Query Where x = $variable How to use NULL or empty string in SQL Rails: Using greater than/less than with a where statement CASE in WHERE, SQL Server How to use "like" and "not like" in SQL MSAccess for the same field? Can the "IN" operator use LIKE-wildcards (%) in Oracle?