Juste pour mettre à jour ceci car il semble que beaucoup de gens y viennent, si vous utilisez Rails 4, regardez les réponses de Trung Lê` et de VinniVidiVicci.
Topic.where.not(forum_id:@forums.map(&:id))
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
J'espère qu'il existe une solution simple n'impliquant pas find_by_sql
, sinon je suppose que cela devra fonctionner.
J'ai trouvé cet article qui fait référence à ceci:
Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })
qui est le même que
SELECT * FROM topics WHERE forum_id IN (<@forum ids>)
Je me demande s’il existe un moyen de faire NOT IN
avec cela, comme:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
J'utilise ceci:
Topic.where('id NOT IN (?)', Array.wrap(actions))
Où actions
est un tableau avec: [1,2,3,4,5]
Modifier:
Pour la notation Rails 4:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Pour info, dans Rails 4, vous pouvez utiliser la syntaxe not
:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Utiliser Arel:
topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(@forum_ids))
ou, si vous préférez:
topics=Topic.arel_table
Topic.where(topics[:forum_id].in(@forum_ids).not)
et depuis Rails 4 sur:
topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(@forum_ids))
Veuillez noter que vous ne souhaitez pas que le forum soit la liste d'identifiants, mais plutôt une sous-requête. Si c'est le cas, vous devriez faire quelque chose comme ceci avant de lire les sujets:
@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
de cette façon, vous obtenez tout dans une seule requête: quelque chose comme:
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
Notez également que vous ne voulez pas faire cela, mais plutôt une jointure - ce qui pourrait être plus efficace.
Vous pouvez essayer quelque chose comme:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.map(&:id)])
Vous pourriez avoir besoin de faire @forums.map(&:id).join(',')
. Je ne me souviens pas si Rails sera l'argument dans une liste CSV si elle est énumérable.
Vous pouvez aussi faire ceci:
# 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)
Pour développer @Trung Lê answer, dans Rails 4, vous pouvez effectuer les opérations suivantes:
Topic.where.not(forum_id:@forums.map(&:id))
Et vous pourriez aller plus loin… .. Si vous devez d’abord filtrer uniquement les sujets publiés et puis filtrer les identifiants que vous ne voulez pas, vous pouvez le faire:
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Rails 4, c'est tellement plus facile!
La solution acceptée échoue si @forums
est vide. Pour contourner cela, je devais faire
Topic.find(:all, :conditions => ['forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id))])
Ou, si vous utilisez Rails 3+:
Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all
La plupart des réponses ci-dessus devraient vous suffire, mais si vous faites beaucoup plus de telles combinaisons de prédicats et complexes, consultez Squeel . Vous pourrez faire quelque chose comme:
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)}
Vous voudrez peut-être jeter un coup d’œil au plugin meta_where d’Ernie Miller. Votre déclaration SQL:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
... pourrait être exprimé comme ceci:
Topic.where(:forum_id.nin => @forum_ids)
Ryan Bates de Railscasts a créé un joli screencast expliquant MetaWhere .
Je ne sais pas si c'est ce que vous recherchez, mais à mes yeux, cela a certainement l'air mieux qu'une requête SQL embarquée.
Ces identifiants de forum peuvent-ils être élaborés de manière pragmatique? par exemple. pouvez-vous trouver ces forums en quelque sorte - si tel est le cas, vous devriez faire quelque chose comme
Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
Ce qui serait plus efficace que de faire un not in
SQL
L'article original mentionnait spécifiquement l'utilisation d'identifiants numériques, mais je suis venu ici pour chercher la syntaxe permettant de faire un NOT IN avec un tableau de chaînes.
ActiveRecord s'en chargera également pour vous:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
Cela optimise la lisibilité, mais n'est pas aussi efficace en termes de requêtes de base de données:
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)
Vous pouvez utiliser SQL dans vos conditions:
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])
Voici une requête plus complexe "pas dans" utilisant une sous-requête dans Rails 4 avec squeel. Bien sûr, très lent comparé à l'équivalent sql, mais bon, ça marche.
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)}
}
Les deux premières méthodes du champ d'application sont d'autres domaines qui déclarent les alias cavtl1 et tl1. << est l'opérateur not in de squeel.
J'espère que ça aide quelqu'un.
Lorsque vous interrogez un tableau vide, ajoutez "<< 0" au tableau du bloc where afin qu'il ne renvoie pas "NULL" et rompe la requête.
Topic.where('id not in (?)',actions << 0)
Si les actions peuvent être un tableau vide ou vide.
Piggybacking de jonnii:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.pluck(:id)])
en utilisant plumer plutôt que la cartographie sur les éléments
trouvé via railsconf 2012 10 choses que vous ne saviez pas que Rails pouvait faire