Рельсы, в которых используется условие NOT NIL

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

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

Я хочу найти, где id НЕ равен нулю. Я пытался:

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

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

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

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


person SooDesuNe    schedule 23.11.2010    source источник
comment
!nil оценивается как true в Ruby, а ARel переводит true в 1 в запросе SQL. Таким образом, сгенерированный запрос на самом деле является тем, о чем вы просили - это не была ошибка ARel.   -  person yuval    schedule 10.02.2017


Ответы (5)


Канонический способ сделать это с помощью 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 })

При работе с областями видимости между таблицами я предпочитаю использовать _4 _ чтобы мне было легче использовать существующие области видимости.

Foo.includes(:bar).merge(Bar.where.not(id: nil))

Кроме того, поскольку includes не всегда выбирает стратегию объединения , вам также следует использовать здесь references, иначе вы может закончиться неверным SQL.

Foo.includes(:bar)
   .references(:bar)
   .merge(Bar.where.not(id: nil))
person Adam Lassek    schedule 23.11.2010
comment
Последний здесь не работает для меня, нужен ли нам дополнительный гем или плагин для этого? Я получаю: rails undefined method 'not_eq' for :confirmed_at:Symbol .. - person Tim Baas; 07.06.2011
comment
@Tim Да, гем MetaWhere, на который я ссылался выше. - person Adam Lassek; 09.06.2011
comment
Мне нравится решение, которое не требует других драгоценных камней :) даже если оно немного некрасиво - person oreoshake; 27.07.2011
comment
@oreoshake MetaWhere / Squeel стоит иметь, это всего лишь крошечный аспект. Но, конечно, полезно знать общий случай. - person Adam Lassek; 27.07.2011
comment
Спасибо, чувак, я ничего не знал о Сквилле. Ваше здоровье. - person Chance; 05.11.2011
comment
Привет, Адам / все, вам нужны и squeel, и metawhere, чтобы делать этот красивый материал, содержащий только рубин, или одного из них достаточно? В чем разница, правда? - person mjnissim; 24.07.2012
comment
И еще: у меня с sqlite3 так не получилось. sqlite3 хочет увидеть field_name != 'NULL'. - person mjnissim; 24.07.2012
comment
@mjnissim MetaWhere был создан для Rails 3.0, Squeel заменил его на Rails ›= 3.1 - person Adam Lassek; 25.07.2012
comment
Я лично стараюсь НИКОГДА не использовать необработанный SQL в моем коде, поэтому я использую Squeel, а также новый синтаксис Arel, как объясняет Райан Бигг ниже. - person jaydel; 31.05.2016
comment
@jaydel: проблема со Squeel в том, что ему нужно постоянное внимание, чтобы иметь дело с критическими изменениями, и не всегда у него есть владелец. Вместо этого я больше перехожу к облегченным помощникам Arel и объектам запросов. - person Adam Lassek; 01.06.2016
comment
@AdamLassek Вы действительно можете сказать, что у него не всегда есть владелец большинства драгоценных камней в мире рубинов. Я думаю, что большинство из нас решает либо доверять драгоценному камню (в определенной степени), либо принять риск, что вам, возможно, придется раскошелиться и исправить то, что вам нужно. - person jaydel; 09.06.2016
comment
@AdamLassek, спасибо, Адам. Что делать, если есть несколько условий where? добавление остальных из них .where (здесь другие условия) не мешало бы производительности? - person BKSpurgeon; 19.08.2016
comment
@BKSpurgeon Цепочка where условий - это просто создание AST, он не попадает в базу данных, пока вы не нажмете метод терминала, такой как each или to_a. Построение запроса не влияет на производительность; то, что вы запрашиваете из базы данных. - person Adam Lassek; 20.08.2016

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

Вот что вам нужно:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))
person Ryan Bigg    schedule 23.11.2010
comment
Мне любопытно, какова тогда логика превращения! Nil в '1' - person SooDesuNe; 23.11.2010
comment
Предположительно! Nil возвращает true, которое является логическим. :id => true дает вам id = 1 в SQLese. - person zetetic; 23.11.2010
comment
Это хороший способ избежать написания сырых фрагментов sql. Однако синтаксис не такой лаконичный, как у Squeel. - person Kelvin; 16.05.2012
comment
Мне не удалось этого сделать с sqlite3. sqlite3 хочет видеть field_name != 'NULL'. - person mjnissim; 24.07.2012
comment
@zetetic Если вы не используете postgres, в этом случае вы получите id = 't' :) - person thekingoftruth; 11.09.2012
comment
Проблема в том, что он! = Null всегда проверяет истинность в SQL, даже если он равен нулю. Да, ребята из rails работали над этим, но использование синтаксиса not_eq только вызывает путаницу, когда вам в конечном итоге нужно выполнить преобразование в SQL. - person masukomi; 09.11.2012
comment
Стоит отметить, что это работает в Rails 3 (в отличие от нескольких ответов). - person Kyle McClellan; 23.04.2020

Для Rails4:

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

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Но для записи, если вам нужно условие «NOT NULL», просто используйте предикат not:

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)

ПРЕДУПРЕЖДЕНИЕ ОБ УСТАРЕВАНИИ: Похоже, вы хотите загрузить таблицу (ы) (одну из: ....), на которые есть ссылка в строковом фрагменте SQL. Например:

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

В настоящее время Active Record распознает таблицу в строке и знает, что нужно ПРИСОЕДИНЯТЬ таблицу комментариев к запросу, а не загружать комментарии в отдельный запрос. Однако выполнение этого без написания полноценного синтаксического анализатора SQL по сути своей ошибочно. Поскольку мы не хотим писать синтаксический анализатор SQL, мы убираем эту функцию. С этого момента вы должны явно указывать Active Record, когда вы ссылаетесь на таблицу из строки:

Post.includes(:comments).where("comments.title = 'foo'").references(:comments)
person Matt Rogish    schedule 06.10.2013
comment
Мне помог звонок references! - person theblang; 20.01.2017

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

Foo.where.not(bar: nil)
person Raed Tulefat    schedule 11.11.2016
comment
Это лучший ответ здесь. - 2017 г. robots.oughttbot.com/activerecords-wherenot - person dezman; 21.07.2017

С Rails 4 это просто:

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

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

person Tilo    schedule 08.08.2014