Wie eine NOT IN-Abfrage mit Active / Rails zum Ausdruck bringen?
-
29-09-2019 - |
Frage
Nur diese zu aktualisieren, da es eine Menge Leute scheint kommen, wenn Sie mit Rails 4 Blick auf den Antworten von Trung Lê` und VinniVidiVicci.
Topic.where.not(forum_id:@forums.map(&:id))
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Ich hoffe es eine einfache Lösung, die nicht find_by_sql
mit sich bringt, wenn nicht, dann denke ich, dass zur Arbeit hat.
Ich habe diesen Artikel , die diese verweist:
Topic.find(:all, :conditions => { :forum_id => @forums.map(&:id) })
, das ist die gleiche wie
SELECT * FROM topics WHERE forum_id IN (<@forum ids>)
Ich frage mich, ob es einen Weg gibt NOT IN
damit zu tun, wie:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
Lösung
Ich verwende diese:
Topic.where('id NOT IN (?)', Array.wrap(actions))
Wo actions
ist ein Array mit: [1,2,3,4,5]
Edit:
Für Rails 4 Notation:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Andere Tipps
FYI, in Schienen 4 können Sie not
Syntax verwenden:
Article.where.not(title: ['Rails 3', 'Rails 5'])
Sie können so etwas wie versuchen:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.map(&:id)])
Sie müssen möglicherweise @forums.map(&:id).join(',')
tun. Ich kann mich nicht daran erinnern, ob Rails wird das Argument in eine CSV-Liste, wenn es abzählbar ist.
Sie können auch dies tun:
# 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)
Mit Arel:
topics=Topic.arel_table
Topic.where(topics[:forum_id].not_in(@forum_ids))
oder, falls bevorzugt:
topics=Topic.arel_table
Topic.where(topics[:forum_id].in(@forum_ids).not)
und da Schienen 4 auf:
topics=Topic.arel_table
Topic.where.not(topics[:forum_id].in(@forum_ids))
Bitte beachten, schließlich wollen Sie nicht die forum_ids die ids Liste sein, sondern eine Unterabfrage, wenn ja, dann sollten Sie etwas tun, bevor die Themen bekommen:
@forum_ids = Forum.where(/*whatever conditions are desirable*/).select(:id)
auf diese Weise Sie alles in einer einzigen Abfrage erhalten: so etwas wie:
select * from topic
where forum_id in (select id
from forum
where /*whatever conditions are desirable*/)
Beachten Sie auch, dass schließlich Sie nicht wollen, dies zu tun, sondern vielmehr eine Join -. Was effizienter sein könnte
erweitern, um auf @Trung Lê Antwort, in Rails 4 können Sie die folgenden Aktionen ausführen:
Topic.where.not(forum_id:@forums.map(&:id))
Und man könnte es noch einen Schritt weiter. Wenn Sie nur die veröffentlichten Themen ersten Filter benötigen und und Filter aus Iden Sie nicht wollen, könnten Sie dies tun:
Topic.where(published:true).where.not(forum_id:@forums.map(&:id))
Schienen 4 macht es so viel einfacher!
Die akzeptierte Lösung schlägt fehl, wenn @forums
leer. Um diese ich umgehen musste tun
Topic.find(:all, :conditions => ['forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id))])
Oder, wenn mit Rails 3 +:
Topic.where( 'forum_id not in (?)', (@forums.empty? ? '' : @forums.map(&:id)) ).all
Die meisten Antworten oben sollten Sie genügen aber wenn Sie viel mehr solcher Prädikat und komplexe Kombinationen überprüfen squeel . Sie werden sich wie zu etwas tun können:
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)}
Sie können einen Blick auf die meta_where Plugin von Ernie Miller haben wollen. Ihre SQL-Anweisung:
SELECT * FROM topics WHERE forum_id NOT IN (<@forum ids>)
... könnte wie folgt ausgedrückt werden:
Topic.where(:forum_id.nin => @forum_ids)
Ryan Bates von Railscasts erstellt einen schönen Screencasts MetaWhere zu erklären.
Nicht sicher, ob dies ist, was Sie suchen, aber in meinen Augen sieht es auf jeden Fall besser als eine eingebettete SQL-Abfrage.
Die ursprüngliche Post erwähnt speziell numerisches IDs verwenden, aber ich kam hier für die Syntax suche einen NOT IN mit einer Reihe von Strings zu tun.
Active wird, dass man einfach nur schön zu:
Thing.where(['state NOT IN (?)', %w{state1 state2}])
Kann man diese Forum-IDs auf pragmatische Weise ausgearbeitet werden? z.B. Sie können diese Foren irgendwie finden - wenn das der Fall ist, sollten Sie so etwas wie
tunTopic.all(:joins => "left join forums on (forums.id = topics.forum_id and some_condition)", :conditions => "forums.id is null")
Welche effizienter wäre als ein tun SQL not in
Auf diese Weise optimiert, um die Lesbarkeit, aber es ist nicht so effizient in Bezug auf den Datenbankabfragen:
# Retrieve all topics, then use array subtraction to
# find the ones not in our list
Topic.all - @forums.map(&:id)
Sie können SQL in Ihren Bedingungen verwenden:
Topic.find(:all, :conditions => [ "forum_id NOT IN (?)", @forums.map(&:id)])
Huckepack off von jonnii:
Topic.find(:all, :conditions => ['forum_id not in (?)', @forums.pluck(:id)])
mit zupfen, anstatt Mapping über die Elemente
gefunden über RailsConf 2012 10 Dinge, die Sie nicht wissen, Schienen tun konnte
Wenn Sie Abfrage ein leeres Array add „<< 0“ auf das Feld in dem where-Block, damit es nicht zurückgeben „NULL“ und brechen Sie die Abfrage aus.
Topic.where('id not in (?)',actions << 0)
Wenn Aktionen könnten eine leere oder leere Array sein.
Hier ist ein komplexer „nicht in“ Abfrage eine Unterabfrage in Schienen 4 mit squeel verwenden. Natürlich sehr langsam in dem entsprechenden SQL-Vergleich, aber hey, es funktioniert.
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)}
}
Die ersten zwei Methoden im Rahmen gibt andere Bereiche, die die Aliase cavtl1 und tl1 erklären. << ist nicht in Bediener in squeel.
Hope dies hilft jemand.