Рельсы: использование больше / меньше, чем с оператором where


Я пытаюсь найти всех пользователей с ID больше, чем 200, но у меня возникли некоторые проблемы с конкретным синтаксисом.

User.where(:id > 200) 

и

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

оба ошиблись.

какие предложения?

7 98

7 ответов:

попробуй такое

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

я только проверил это в Rails 4, но есть интересный способ использовать диапазон с where хэш, чтобы получить такое поведение.

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

будет генерировать SQL

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

то же самое можно сделать менее чем с -Float::INFINITY.

я только что опубликовал аналогичный вопрос, спрашивающий об этом с датами так.

>= vs >

чтобы избежать людей, которые должны копать и следовать комментарии разговор Вот основные моменты.

метод выше только создает >= запрос и не a >. Есть много способов справиться с этой альтернативой.

для дискретных чисел

вы можете использовать number_you_want + 1 стратегия, как и выше, где я заинтересован в пользователях с id > 200 но на самом деле искать id >= 201. Это хорошо для целых чисел и чисел, где вы можете увеличить на одну единицу интерес.

если у вас есть число, извлеченное в хорошо названную константу, это может быть самым простым для чтения и понимания с первого взгляда.

перевернутая логика

мы можем использовать тот факт, что x > y == !(x <= y) и использовать где не цепь.

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 будет выпущен (25 декабря 2018 года), вы сможете использовать новый синтаксис бесконечного диапазона! Вместо 201..Float::INFINITY вы сможете просто писать 201... Подробнее в этой статье.

лучше использовать для создания области в пользовательской модели where(arel_table[:id].gt(id))

Если вы хотите более интуитивное письмо, он существует драгоценный камень под названием squeel это позволит вам написать свою инструкцию следующим образом:

User.where{id > 200}

обратите внимание на символы "скобки" { } и id просто текст.

все, что вам нужно сделать, это добавить squeel в свой Gemfile:

gem "squeel"

Это может облегчить вашу жизнь много при написании сложного оператора SQL в Ruby.

Арел-твой друг.

пользователей.где(пользователей.arel_table[:ИД].ГТ(200))

у меня часто возникает эта проблема с полями дат (где операторы сравнения очень распространены).

чтобы подробнее остановиться на ответе Михая, который я считаю солидным подходом.

к моделям вы можете добавить области, как это:

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 будет обрабатывать эту часть генерации запроса.

короче:

User.where("id > 200")