Рельсы, где условие с использованием NOT NULL

298

Используя стиль рельсов 3, как бы я написал противоположное:

Foo.includes(:bar).where(:bars=>{:id=>nil})

Я хочу найти, где id не равно null. Я пробовал:

Foo.includes(:bar).where(:bars=>{:id=>!nil}).to_sql

Но это возвращает:

=> "SELECT     \"foos\".* FROM       \"foos\"  WHERE  (\"bars\".\"id\" = 1)"

Это определенно не то, что мне нужно, и почти похоже на ошибку в AREL.

  • 2
    !nil оценивает значение true в Ruby, а ARel переводит значение true в 1 в запросе SQL. Таким образом, сгенерированный запрос на самом деле является тем, что вы просили - это не ошибка ARel.
Теги:
rails-activerecord
arel

5 ответов

439
Лучший ответ

Канонический способ сделать это с помощью Rails 3:

Foo.includes(:bar).where("bars.id IS NOT NULL")

ActiveRecord 4.0 и выше добавляет where.not, чтобы вы могли сделать это:

Foo.includes(:bar).where.not('bars.id' => nil)
Foo.includes(:bar).where.not(bars: { id: nil })

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

  • 1
    Последний здесь не работает для меня, нам нужен дополнительный гем или плагин для этого? Я получаю: rails undefined method 'not_eq' for :confirmed_at:Symbol ..
  • 3
    @Tim Да, драгоценный камень MetaWhere, который я связал выше.
Показать ещё 12 комментариев
229

Это не ошибка в AREL, это ошибка в вашей логике.

Здесь вы хотите:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))
  • 2
    Мне любопытно, какова логика превращения! Ноль в «1»
  • 12
    В предположении,! Nil возвращает true , что является логическим значением. :id => true возвращает вам id = 1 в SQLese.
Показать ещё 6 комментариев
35

Для Rails4:

Итак, то, что вы хотите, является внутренним соединением, поэтому вам действительно нужно использовать предикат объединения:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Но для записи, если вы хотите условие "NOT NULL", просто используйте не предикат:

Foo.includes(:bar).where.not(bars: {id: nil})

Select * from Foo Left Outer Join Bars on .. WHERE bars.id IS NOT NULL

Обратите внимание, что этот синтаксис сообщает об устаревании (он говорит о фрагменте строки SQL, но я предполагаю, что условие хеша изменяется на строку в синтаксическом анализаторе?), поэтому не забудьте добавить ссылки в конец:

Foo.includes(:bar).where.not(bars: {id: nil}).references(:bar)

ПРЕДУПРЕЖДЕНИЕ О ДЕПРЕКАЦИИ: похоже, что вы загружаете таблицу (один of:....), на которые ссылаются строковый фрагмент SQL. Например:

Post.includes(:comments).where("comments.title = 'foo'")

В настоящее время Active Record распознает таблицу в строке и знает чтобы присоединиться к таблице комментариев к запросу, а не загружать комментарии в отдельном запросе. Однако, делая это без написания полномасштабного SQL-парсер по своей сути ошибочен. Поскольку мы не хотим писать SQL парсер, мы удаляем эту функциональность. Отныне вы должны явно указывать Active Record, когда вы ссылаетесь на таблицу из строка:

Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
  • 1
    references звонок помог мне!
21

С Rails 4 легко:

 Foo.includes(:bar).where.not(bars: {id: nil})

См. также:   http://guides.rubyonrails.org/active_record_querying.html#not-conditions

8

Не уверен, что это полезно, но это то, что сработало для меня в Rails 4

Foo.where.not(bar: nil)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню