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




query left (13)

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

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

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)

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.


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

¿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


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)

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.


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


Puedes usar sql en tus condiciones:

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

También necesita reemplazar sus índices:

class RenameOldTableToNewTable< ActiveRecord:Migration
  def self.up
    remove_index :old_table_name, :column_name
    rename_table :old_table_name, :new_table_name
    add_index :new_table_name, :column_name
  end 

  def self.down
    remove_index :new_table_name, :column_name
    rename_table :new_table_name, :old_table_name
    add_index :old_table_name, :column_name
  end
end

Y renombra tus archivos, etc. manualmente como lo describen otras respuestas aquí.

Consulte: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

Asegúrese de que puede retroceder y avanzar después de escribir esta migración. Puede ser complicado si se equivoca y se atasca con una migración que intenta realizar algo que ya no existe. Mejor elimine la basura de toda la base de datos y comience de nuevo si no puede retroceder. Así que ten en cuenta que es posible que necesites hacer una copia de seguridad de algo.

También: verifique schema_db para ver si hay nombres de columnas relevantes en otras tablas definidas por has_ ​​o belongs_to o algo así. Probablemente necesites editarlos también.

Y finalmente, hacer esto sin un conjunto de pruebas de regresión sería una locura.





ruby-on-rails activerecord