ActiveRecord Requête Union
J'ai écrit un couple de requêtes complexes (au moins pour moi) avec Ruby on Rails interface de requête:
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})
Ces deux requêtes fonctionnent très bien par eux-mêmes. Les deux retour de courrier objets. Je tiens à associer à ces postes en un seul ActiveRelation. Car il pourrait y avoir des centaines de milliers de postes à un certain point, ce qui doit être fait au niveau base de données. Si il s'agissait d'une requête MySQL, je pourrais simplement de l'utilisateur de la UNION
de l'opérateur. Quelqu'un sait si je peux faire quelque chose de similaire avec RoR de l'interface de requête?
- Vous devriez être en mesure d'utiliser portée. Créer 2 champs d'application et ensuite appeler comme
Post.watched_news_posts.watched_topic_posts
. Vous devrez peut-être envoyer dans les params du étendues pour des choses comme la:user_id
et:topic
. - Merci pour la suggestion. Selon les docs, "Un champ d'application représente une réduction d'une requête de base de données". Dans mon cas, je ne suis pas à la recherche, pour des postes qui sont à la fois watched_news_posts et watched_topic_posts. Plutôt, je suis à la recherche pour les postes qui sont en watched_news_posts ou watched_topic_posts, avec pas de doublons autorisés. Est-ce encore possible de le faire avec des étendues?
- Pas vraiment possible, out-of-the-box. Il existe un plugin sur github appelé l'union, mais il utilise de la vieille école de la syntaxe (méthode de la classe et de hachage requête de style params), si c'est cool avec vous, je dirais aller avec elle... sinon écrire le chemin le plus long dans un find_by_sql dans votre périmètre.
- Je suis d'accord avec jenjenut233, et je pense que vous pourriez faire quelque chose comme
find_by_sql("#{watched_news_posts.to_sql} UNION #{watched_topic_posts.to_sql}")
. Je n'ai pas testé que, alors laissez-moi savoir comment ça se passe si vous l'essayez. Aussi, il y a probablement quelques ARel fonctionnalité qui serait à l'œuvre. - Eh bien j'ai réécrit les requêtes que des requêtes SQL. Ils travaillent maintenant, mais malheureusement
find_by_sql
ne peut pas être utilisé avec d'autres chainable requêtes, ce qui signifie que j'ai maintenant réécrire mon will_paginate des filtres et des requêtes ainsi. Pourquoi ne pas ActiveRecord soutenir ununion
opération? - Vous devriez être en mesure d'accomplir ceci avec une jointure externe gauche
Vous devez vous connecter pour publier un commentaire.
Voici un petit module que j'ai écrit qui permet à l'UNION de plusieurs étendues. Il renvoie également les résultats d'une instance de ActiveRecord::Relation.
Voici l'essentiel: https://gist.github.com/tlowrimore/5162327
Edit:
Comme demandé, voici un exemple de la façon dont UnionScope travaux:
union_scope
définition explicitement sélectionne leid
colonne et rien de plus. Je serais curieux de voir ce que le SQL qui en résulte est pour votre scénario. Avez-vous essayé d'appelerto_sql
à votre portée pour voir ce que le SQL généré ressemble?J'ai aussi rencontré ce problème, et maintenant, ma stratégie est de générer du SQL (à la main ou à l'aide de
to_sql
sur une étendue existante), puis le coller dans lafrom
clause. Je ne peux pas garantir qu'elle est plus efficace que votre méthode acceptée, mais il est relativement facile sur les yeux et vous donne une normale ARel objet.Vous pouvez le faire avec deux modèles différents, mais vous devez vous assurer que les deux "ressemble" à l'intérieur de l'UNION, vous pouvez l'utiliser
select
sur les deux requêtes pour vous assurer qu'ils produisent les mêmes colonnes.#or
dans mon Rails 4 du projet. En supposant même modèle:klass.from("(#{to_sql} union #{other_relation.to_sql}) as #{table_name}")
Basé sur les Olives de réponse, je n'ai trouvé d'autre solution à ce problème. Il se sent un peu comme un hack, mais elle renvoie une instance de
ActiveRelation
, qui est ce que je recherchais en premier lieu.Je serais toujours reconnaissant si quelqu'un a des suggestions pour optimiser ce ou d'améliorer les performances, parce que c'est essentiellement l'exécution de trois requêtes et se sent un peu redondant.
Comment sur...
pluck
appel d'une requête en elle-même..order
ou.paginate
méthodes... Il maintient l'orm classesVous pouvez également utiliser Brian Hempel's active_record_union joyau qui s'étend
ActiveRecord
avec ununion
méthode pour les étendues.Votre requête serait comme ceci:
J'espère que ce sera finalement fusionné
ActiveRecord
un jour.Pourriez-vous utiliser un OU à la place d'un SYNDICAT?
Alors vous pourriez faire quelque chose comme:
(Puisque vous êtes rejoint le regardé table deux fois, je ne suis pas trop sûr de ce que les noms de tables seront pour la requête)
Car il ya beaucoup de jointures, il pourrait aussi être assez lourd sur la base de données, mais il pourrait être en mesure d'être optimisé.
Sans doute, ce qui améliore la lisibilité, mais pas nécessairement de la performance:
Cette méthode retourne un ActiveRecord::Relation, de sorte que vous pouvez l'appeler comme ceci:
Il y a un active_record_union gem.
Pourrait être utile
https://github.com/brianhempel/active_record_union
SELECT "posts".* FROM (
SELECT "posts".* FROM "posts" WHERE "posts"."user_id" = 1
UNION
SELECT "posts".* FROM "posts" WHERE (published_at < '2014-07-19 16:04:21.918366')
) posts
Je voudrais juste exécuter deux requêtes dont vous avez besoin et combiner les tableaux d'enregistrements sont renvoyés:
Ou, à tout le moins de le tester. Pensez-vous que le tableau de combinaison en ruby sera beaucoup trop lent? En regardant les requêtes suggérées pour contourner le problème, je ne suis pas convaincu qu'il y va y avoir beaucoup de différence en termes de performances.
Dans un cas similaire j'ai résumé deux tableaux, et utilisé
Kaminari:paginate_array()
. Très agréable et la solution de travail. J'ai été incapable d'utiliserwhere()
, parce que j'ai besoin de la somme de deux résultats différents avec lesorder()
sur la même table.Elliot Nelson répondu bon, sauf le cas où les relations sont vides. Je voudrais faire quelque chose comme ça:
fin
Heres comment j'ai rejoint les requêtes SQL à l'aide de l'UNION sur mon propre ruby on rails application.
Vous pouvez utiliser le dessous comme source d'inspiration sur votre propre code.
Ci-dessous est l'UNION où j'ai rejoint les étendues ensemble.
Moins de problèmes et plus facile à suivre:
Donc en fin de compte: