update rails query left joins includes has_and_belongs_to_many active ruby-on-rails activerecord

ruby on rails - query - ¿Cómo se expresa una consulta NOT IN con ActiveRecord/Rails?



rails sql query (15)

¿Pueden estos identificadores de foro elaborarse de forma pragmática? Por ejemplo, ¿puede encontrar estos foros de alguna manera? Si ese es el caso, debe hacer algo como

Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")

Que sería más eficiente que hacer un SQL not in

Solo para actualizar esto ya que parece que mucha gente viene a esto, si está utilizando Rails 4, mire las respuestas de Trung Lê` y VinniVidiVicci.

Topic.where.not(forum_id:@forums.map(&:id)) Topic.where(published:true).where.not(forum_id:@forums.map(&:id))

Espero que haya una solución fácil que no involucre find_by_sql , si no, entonces supongo que tendrá que funcionar.

Encontré este artículo que hace referencia a esto:

Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })

que es lo mismo que

SELECT * FROM topics WHERE forum_id IN (<@forum ids>)

Me pregunto si hay una manera de NOT IN con eso, como:

SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)


Aquí hay una consulta más compleja "no disponible", usando una subconsulta en rieles 4 usando squeel. Por supuesto, es muy lento en comparación con el sql equivalente, pero bueno, funciona.

scope :translations_not_in_english, ->(calmapp_version_id, language_iso_code){ join_to_cavs_tls_arr(calmapp_version_id). joins_to_tl_arr. where{ tl1.iso_code == ''en'' }. where{ cavtl1.calmapp_version_id == my{calmapp_version_id}}. where{ dot_key_code << (Translation. join_to_cavs_tls_arr(calmapp_version_id). joins_to_tl_arr. where{ tl1.iso_code == my{language_iso_code} }. select{ "dot_key_code" }.all)} }

Los primeros 2 métodos en el alcance son otros ámbitos que declaran los alias cavtl1 y tl1. << es el operador no en squeel.

Espero que esto ayude a alguien.


Cuando consulta una matriz en blanco, agregue "<< 0" a la matriz en el bloque where para que no devuelva "NULO" y rompa la consulta.

Topic.where(''id not in (?)'',actions << 0)

Si las acciones pueden ser una matriz vacía o en blanco.


De esta manera se optimiza para la legibilidad, pero no es tan eficiente en términos de consultas a la base de datos:

# Retrieve all topics, then use array subtraction to # find the ones not in our list Topic.all - @forums.map(&:id)


Es posible que desee echarle un vistazo al complemento meta_where de Ernie Miller. Su declaración SQL:

SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)

... podría expresarse así:

Topic.where(:forum_id.nin => @forum_ids)

Ryan Bates de Railscasts creó un buen screencast explicando MetaWhere .

No estoy seguro de si esto es lo que está buscando, pero a mis ojos ciertamente se ve mejor que una consulta SQL incrustada.


Estoy usando esto:

Topic.where(''id NOT IN (?)'',actions)

Donde las actions son una matriz con: [1,2,3,4,5]

Editar:

Para la notación Rails 4:

Article.where.not(title: [''Rails 3'', ''Rails 5''])


FYI, en Rails 4, puede usar not sintaxis:

Article.where.not(title: [''Rails 3'', ''Rails 5''])


La mayoría de las respuestas anteriores deberían bastarle, pero si está haciendo mucho más de ese predicado y combinaciones complejas, consulte Squeel . Podrás hacer algo como:

Topic.where{{forum_id.not_in => @forums.map(&:id)}} Topic.where{forum_id.not_in @forums.map(&:id)} Topic.where{forum_id << @forums.map(&:id)}


La publicación original menciona específicamente el uso de ID numéricos, pero vine aquí en busca de la sintaxis para hacer NOT IN con una matriz de cadenas.

ActiveRecord lo manejará muy bien también:

Thing.where([''state NOT IN (?)'', %w{state1 state2}])


La solución aceptada falla si @forums está vacío. Para solucionar esto, tuve que hacer

Topic.find(:all, :conditions => [''forum_id not in (?)'', (@forums.empty? ? '''' : @forums.map(&:id))])

O, si usa Rails 3+:

Topic.where( ''forum_id not in (?)'', (@forums.empty? ? '''' : @forums.map(&:id)) ).all


Para expandir @Trung Lê answer, en Rails 4 puede hacer lo siguiente:

Topic.where.not(forum_id:@forums.map(&:id))

Y podrías dar un paso más. Si primero necesita filtrar solo los temas publicados y luego filtra los ID que no desea, puede hacer esto:

Topic.where(published:true).where.not(forum_id:@forums.map(&:id))

¡Rails 4 lo hace mucho más fácil!



Puedes probar algo como:

Topic.find(:all, :conditions => [''forum_id not in (?)'', @forums.map(&:id)])

Es posible que deba hacer @forums.map(&:id).join('','') . No puedo recordar si Rails incluirá el argumento en una lista CSV si es enumerable.

También puedes hacer esto:

# in topic.rb named_scope :not_in_forums, lambda { |forums| { :conditions => [''forum_id not in (?)'', forums.select(&:id).join('','')] } # in your controller Topic.not_in_forums(@forums)


Puedes usar sql en tus condiciones:

Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])


Usando Arel:

topics=Topic.arel_table Topic.where(topics[:forum_id].not_in(@forum_ids))

o, si se prefiere:

topics=Topic.arel_table Topic.where(topics[:forum_id].in(@forum_ids).not)

y desde los rieles 4 en:

topics=Topic.arel_table Topic.where.not(topics[:forum_id].in(@forum_ids))

Tenga en cuenta que eventualmente no desea que los forum_ids sean la lista de ids, sino más bien una subconsulta, en caso afirmativo, debe hacer algo como esto antes de obtener los temas:

@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)

de esta manera, obtienes todo en una sola consulta: algo así como:

select * from topic where forum_id in (select id from forum where /*whatever conditions are desirable*/)

También note que eventualmente no quiere hacer esto, sino una unión, lo que podría ser más eficiente.