Ich habe einige komplexe Abfragen (zumindest für mich) mit der Abfrageoberfläche von Ruby on Rail geschrieben:
watched_news_posts = Post.joins(:news => :watched).where(:watched => {:user_id => id})
watched_topic_posts = Post.joins(:post_topic_relationships => {:topic => :watched}).where(:watched => {:user_id => id})
Beide Abfragen funktionieren für sich. Beide geben Post-Objekte zurück. Ich möchte diese Beiträge in einer einzigen ActiveRelation kombinieren. Da es irgendwann Hunderttausende von Posts geben kann, muss dies auf Datenbankebene erfolgen. Wenn es eine MySQL-Abfrage wäre, könnte ich einfach den UNION
Operator verwenden. Weiß jemand, ob ich mit der Abfrageschnittstelle von RoR etwas Ähnliches tun kann?
ruby-on-rails
activerecord
union
active-relation
LandonSchropp
quelle
quelle
Post.watched_news_posts.watched_topic_posts
. Möglicherweise müssen Sie Parameter für Dinge wie:user_id
und an die Bereiche senden:topic
.find_by_sql("#{watched_news_posts.to_sql} UNION #{watched_topic_posts.to_sql}")
. Ich habe das noch nicht getestet. Lassen Sie mich wissen, wie es geht, wenn Sie es versuchen. Außerdem gibt es wahrscheinlich einige ARel-Funktionen, die funktionieren würden.find_by_sql
nicht mit anderen verkettbaren Abfragen verwendet werden, was bedeutet, dass ich jetzt auch meine will_paginate-Filter und Abfragen neu schreiben muss. Warum unterstützt ActiveRecord eineunion
Operation nicht?Antworten:
Hier ist ein kurzes kleines Modul, das ich geschrieben habe und mit dem Sie mehrere Bereiche mit UNION verbinden können. Die Ergebnisse werden auch als Instanz von ActiveRecord :: Relation zurückgegeben.
Hier ist das Wesentliche: https://gist.github.com/tlowrimore/5162327
Bearbeiten:
Hier ist ein Beispiel für die Funktionsweise von UnionScope:
quelle
Ich bin auch auf dieses Problem gestoßen, und jetzt besteht meine Strategie darin, SQL (von Hand oder
to_sql
in einem vorhandenen Bereich) zu generieren und es dann in diefrom
Klausel einzufügen. Ich kann nicht garantieren, dass es effizienter ist als Ihre akzeptierte Methode, aber es ist relativ augenschonend und gibt Ihnen ein normales ARel-Objekt zurück.Sie können dies auch mit zwei verschiedenen Modellen tun, aber Sie müssen sicherstellen, dass beide innerhalb der UNION "gleich aussehen". Sie können
select
beide Abfragen verwenden, um sicherzustellen, dass sie dieselben Spalten erzeugen.quelle
#or
in meinem Rails 4-Projekt zu unterstützen. Angenommen, das gleiche Modell:klass.from("(#{to_sql} union #{other_relation.to_sql}) as #{table_name}")
Basierend auf der Antwort von Olives habe ich eine andere Lösung für dieses Problem gefunden. Es fühlt sich ein bisschen wie ein Hack an, aber es gibt eine Instanz von zurück
ActiveRelation
, nach der ich überhaupt gesucht habe.Ich würde es immer noch begrüßen, wenn jemand Vorschläge hat, dies zu optimieren oder die Leistung zu verbessern, da es im Wesentlichen drei Abfragen ausführt und sich etwas überflüssig anfühlt.
quelle
Sie können auch verwenden , Brian Hempel ‚s active_record_union Juwel , das sich
ActiveRecord
mit einemunion
für Tive Verfahren.Ihre Anfrage würde so aussehen:
Hoffentlich wird dies irgendwann in
ActiveRecord
einem Tag zusammengeführt.quelle
Wie wäre es mit...
quelle
pluck
Aufruf eine Abfrage für sich ist..order
oder.paginate
Methoden verwenden ... Es behält die orm-KlassenKönnten Sie ein ODER anstelle einer UNION verwenden?
Dann könnten Sie so etwas tun wie:
(Da Sie zweimal der überwachten Tabelle beitreten, bin ich mir nicht sicher, wie die Namen der Tabellen für die Abfrage lauten werden.)
Da es viele Verknüpfungen gibt, ist die Datenbank möglicherweise sehr stark belastet, kann jedoch optimiert werden.
quelle
Dies verbessert wohl die Lesbarkeit, aber nicht unbedingt die Leistung:
Diese Methode gibt eine ActiveRecord :: Relation zurück, sodass Sie sie folgendermaßen aufrufen können:
quelle
Es gibt ein Juwel aus active_record_union. Könnte hilfreich sein
https://github.com/brianhempel/active_record_union
quelle
Ich würde nur die zwei Abfragen ausführen, die Sie benötigen, und die Arrays der zurückgegebenen Datensätze kombinieren:
Oder zumindest testen. Denken Sie, dass die Array-Kombination in Ruby viel zu langsam sein wird? Wenn ich mir die vorgeschlagenen Fragen ansehe, um das Problem zu umgehen, bin ich nicht davon überzeugt, dass es einen so signifikanten Leistungsunterschied geben wird.
quelle
In einem ähnlichen Fall habe ich zwei Arrays summiert und verwendet
Kaminari:paginate_array()
. Sehr schöne und funktionierende Lösung. Ich konnte nicht verwendenwhere()
, da ich zwei Ergebnisse mit unterschiedlichen Ergebnissenorder()
in derselben Tabelle summieren muss .quelle
Weniger Probleme und leichter zu folgen:
Also am Ende:
quelle
scopes.drop(1).reduce(where(id: scopes.first)) { |query, scope| query.or(where(id: scope)) }
Thx!Elliot Nelson antwortete gut, außer in dem Fall, in dem einige der Beziehungen leer sind. Ich würde so etwas tun:
Ende
quelle
Hier ist, wie ich SQL-Abfragen mit UNION in meiner eigenen Ruby on Rails-Anwendung verbunden habe.
Sie können das Folgende als Inspiration für Ihren eigenen Code verwenden.
Unten ist die UNION, in der ich die Bereiche zusammengefügt habe.
quelle