Trilhos onde condição usando NOT NIL


359

Usando o estilo Rails 3, como eu escreveria o oposto de:

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

Quero descobrir onde o ID NÃO é nulo. Eu tentei:

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

Mas isso retorna:

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

Definitivamente, não é disso que eu preciso, e quase parece um bug no ARel.


2
!nilAvalia como trueem Ruby, e Arel traduz truea 1em uma consulta SQL. Portanto, a consulta gerada é de fato o que você solicitou - não foi um bug do ARel.
yuval

Respostas:


510

A maneira canônica de fazer isso com o Rails 3:

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

O ActiveRecord 4.0 e superior adiciona where.notpara que você possa fazer isso:

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

Ao trabalhar com escopos entre tabelas, prefiro aproveitar mergepara poder usar os escopos existentes com mais facilidade.

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

Além disso, como includesnem sempre escolhe uma estratégia de associação, você deve usar referencesaqui também, caso contrário, poderá acabar com SQL inválido.

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

11
O último aqui não está funcionando para mim, precisamos de uma gema ou plugin extra para isso? Eu recebo: rails undefined method 'not_eq' for :confirmed_at:Symbol..
Tim Baas

3
@ Tim Sim, a jóia MetaWhere eu liguei acima.
Adam Lassek

3
I como a solução que não requer outras pedras preciosas :) mesmo que seja um pouco feio
oreoshake

11
Vale a pena ter @oreoshake MetaWhere / Squeel, essa é apenas uma pequena faceta. Mas é claro que é bom saber um caso geral.
Adam Lassek

11
As wherecondições do @BKSpurgeon Chaining são simplesmente a construção de um AST, ele não atinge o banco de dados até que você atinja um método terminal como eachou to_a. Construir a consulta não é uma preocupação de desempenho; o que você está solicitando do banco de dados é.
Adam Lassek 20/08/16

251

Não é um bug no ARel, é um bug na sua lógica.

O que você quer aqui é:

Foo.includes(:bar).where(Bar.arel_table[:id].not_eq(nil))

2
Estou curioso para
saber

12
Em suposição,! Nil retorna true, que é um booleano. :id => trueleva você id = 1ao SQLese.
Zetetic

Essa é uma boa maneira de evitar a gravação de fragmentos sql brutos. A sintaxe não é tão concisa quanto o Squeel.
Kelvin #

11
Não consegui fazer isso com o sqlite3. sqlite3 quer ver field_name != 'NULL'.
Mjnissim

@zetetic A menos que você estiver usando postgres, caso em que você começa id = 't':)
thekingoftruth

36

Para Rails4:

Então, o que você quer é uma junção interna, então você realmente deve usar o predicado de junções:

  Foo.joins(:bar)

  Select * from Foo Inner Join Bars ...

Mas, para o registro, se você deseja uma condição "NOT NULL", basta usar o não predicado:

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

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

Observe que essa sintaxe relata uma descontinuação (ela fala sobre um trecho de código SQL, mas acho que a condição de hash foi alterada para string no analisador?), Portanto, adicione as referências ao final:

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

AVISO DE DEPRECAÇÃO: Parece que você está ansioso para carregar a (s) tabela (s) (uma de: ....) que são referenciadas em um snippet SQL de sequência. Por exemplo:

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

Atualmente, o Active Record reconhece a tabela na string e sabe JOIN a tabela de comentários na consulta, em vez de carregar comentários em uma consulta separada. No entanto, fazer isso sem gravar um analisador SQL completo é inerentemente defeituoso. Como não queremos escrever um analisador SQL, estamos removendo essa funcionalidade. A partir de agora, você deve informar explicitamente o Active Record ao fazer referência a uma tabela a partir de uma sequência:

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

11
A referencesligação me ajudou!
theblang


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.