ruby-on-rails - query - rails update




¿Cómo se expresa una consulta NOT IN con ActiveRecord/Rails? (10)

¿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.


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.


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 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)])




activerecord