Rails 4を使用している場合、多くの人々がこれに来るように見えるので、これを更新するために、TrungLê`とVinniVidiVicciの回答を見てください。
Topic.where.not(forum_id:@forums.map(&:id))
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
find_by_sql
を含まない簡単な解決策があることを望んでいます。
これを参照する この記事 を見つけました。
Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })
と同じです
SELECT * FROM topics WHERE forum_id IN (<@forum ids>)
それでNOT IN
を行う方法があるかどうか疑問に思っています:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
私はこれを使用しています:
Topic.where('id NOT IN (?)', Array.wrap(actions))
ここで、actions
は次の配列です:[1,2,3,4,5]
編集:
Rails 4表記の場合:
Article.where.not(title: ['Rails 3', 'Rails 5'])
参考までに、Rails 4では、not
構文を使用できます。
Article.where.not(title: ['Rails 3', 'Rails 5'])
Arelの使用:
topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(@forum_ids))
または、必要に応じて:
topics=Topic.arel_table
Topic.where(topics[:forum_id].in(@forum_ids).not)
Rails 4以降:
topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(@forum_ids))
最終的に、フォーラムIDをIDリストではなくサブクエリにしたい場合は、トピックを取得する前に次のようにする必要があることに注意してください。
@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
このようにして、すべてを単一のクエリで取得できます。
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
また、最終的にはこれを行いたくないが、むしろ結合することに注意してください-より効率的かもしれません
次のようなものを試すことができます:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.map(&:id)])
@forums.map(&:id).join(',')
を実行する必要があるかもしれません。列挙可能な場合、Railsが引数をCSVリストに含めるかどうかは思い出せません。
これを行うこともできます:
# 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)
@TrungLêの回答を展開するには、Rails 4で次のことができます。
Topic.where.not(forum_id:@forums.map(&:id))
そして、さらに一歩踏み出すことができます。最初に公開されたトピックのみをフィルタリングし、不要なIDをthenでフィルタリングする必要がある場合、これを行うことができます。
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Rails 4を使用すると、非常に簡単になります。
@forums
が空の場合、受け入れられたソリューションは失敗します。これを回避するには、私がしなければなりませんでした
Topic.find(:all, :conditions => ['forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id))])
または、Rails 3+を使用している場合:
Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all
上記の答えの大部分で十分ですが、このような述語と複雑な組み合わせをさらに多く行う場合は、 Squeel を確認してください。次のようなことができるようになります。
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)}
Ernie Millerによる meta_whereプラグイン をご覧ください。 SQLステートメント:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
...このように表現できます:
Topic.where(:forum_id.nin => @forum_ids)
RailscastsのRyan Batesが MetaWhereを説明する素敵なスクリーンキャスト を作成しました。
これがあなたが探しているものかどうかはわかりませんが、私の目には確かに埋め込みSQLクエリよりも良く見えます。
これらのフォーラムIDは、実用的な方法で解決できますか?例えばどういうわけかこれらのフォーラムを見つけることができます-もしそうなら、あなたは次のようなことをすべきです
Topic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
SQL not in
を実行するよりも効率的です
元の投稿では、数値IDの使用について具体的に言及していますが、文字列の配列でNOT INを実行するための構文を探してここに来ました。
ActiveRecordもあなたのためにそれをうまく処理します:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
この方法は読みやすさのために最適化されますが、データベースクエリに関しては効率的ではありません。
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)
条件でsqlを使用できます。
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])
Squeelを使用してRails 4のサブクエリを使用する、より複雑な「not in」クエリを次に示します。もちろん、同等のSQLと比較して非常に遅いですが、ちょっと、それは動作します。
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)}
}
スコープ内の最初の2つのメソッドは、エイリアスcavtl1およびtl1を宣言する他のスコープです。 <<は、squeelのnot in演算子です。
これが誰かを助けることを願っています。
空の配列をクエリする場合、whereブロックの配列に「<< 0」を追加して、「NULL」を返さないようにしてクエリを中断します。
Topic.where('id not in (?)',actions << 0)
アクションが空または空の配列である可能性がある場合。
Jonniiからの便乗:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.pluck(:id)])
要素をマッピングするのではなく、pluckを使用する