C'est plus une question "pourquoi les choses fonctionnent de cette façon" plutôt qu'une question "je ne sais pas comment faire" ...
Donc, l'évangile sur l'extraction des enregistrements associés que vous savez que vous allez utiliser est d'utiliser :include
parce que vous obtiendrez une jointure et évitez tout un tas de requêtes supplémentaires:
Post.all(:include => :comments)
Cependant, lorsque vous regardez les journaux, aucune jointure ne se produit:
Post Load (3.7ms) SELECT * FROM "posts"
Comment Load (0.2ms) SELECT "comments.*" FROM "comments"
WHERE ("comments".post_id IN (1,2,3,4))
ORDER BY created_at asc)
Il est prend un raccourci car il tire tous les commentaires à la fois, mais il est toujours pas une jointure (qui est ce que toute la documentation semble dire). La seule façon d'obtenir une jointure est d'utiliser à la :joins
place de :include
:
Post.all(:joins => :comments)
Et les journaux montrent:
Post Load (6.0ms) SELECT "posts".* FROM "posts"
INNER JOIN "comments" ON "posts".id = "comments".post_id
Suis-je en train de manquer quelque chose? J'ai une application avec une demi-douzaine d'associations et sur un écran, j'affiche les données de chacune d'entre elles. Il semble qu'il serait préférable d'avoir une requête jointe au lieu de 6 individus. Je sais qu'en termes de performances, il n'est pas toujours préférable de faire une jointure plutôt que des requêtes individuelles (en fait, si vous passez par le temps passé, il semble que les deux requêtes individuelles ci-dessus soient plus rapides que la jointure), mais après tous les documents J'ai lu que je suis surpris de :include
ne pas travailler comme annoncé.
Peut-être que Rails est conscient du problème de performances et ne se joint pas, sauf dans certains cas?
la source
includes
(pour tous ceux qui liront ceci)Réponses:
Il semble que la
:include
fonctionnalité a été modifiée avec Rails 2.1. Dans tous les cas, Rails effectuait la jointure, mais pour des raisons de performances, il a été modifié pour utiliser plusieurs requêtes dans certaines circonstances. Ce billet de blog de Fabio Akita contient de bonnes informations sur le changement (voir la section intitulée "Chargement optimisé et désireux").la source
.joins
rejoint simplement les tables et apporte en retour les champs sélectionnés. si vous appelez des associations sur le résultat de la requête de jointure, il lancera à nouveau les requêtes de base de données:includes
sera impatient de charger les associations incluses et de les ajouter en mémoire.:includes
charge tous les attributs de tables inclus. Si vous appelez des associations sur le résultat de la requête d'inclusion, il ne déclenchera aucune requêtela source
La différence entre les jointures et les inclusions est que l'utilisation de l'instruction include génère une requête SQL beaucoup plus volumineuse chargeant en mémoire tous les attributs des autres tables.
Par exemple, si vous avez un tableau plein de commentaires et que vous utilisez un: joins => utilisateurs pour extraire toutes les informations utilisateur à des fins de tri, etc. cela fonctionnera bien et prendra moins de temps que: inclure, mais dites que vous voulez afficher le commentaire ainsi que le nom de l'utilisateur, l'adresse e-mail, etc. Pour obtenir les informations à l'aide de: jointures, il devra effectuer des requêtes SQL distinctes pour chaque utilisateur qu'il récupère, alors que si vous avez utilisé: include, ces informations sont prêtes à l'emploi.
Excellent exemple:
http://railscasts.com/episodes/181-include-vs-joins
la source
Je lisais récemment plus sur la différence entre
:joins
et:includes
dans les rails. Voici une explication de ce que j'ai compris (avec des exemples :))Considérez ce scénario:
Un utilisateur a_de nombreux commentaires et un commentaire appartient_ à un utilisateur.
Le modèle utilisateur a les attributs suivants: nom (chaîne), âge (entier). Le modèle Comment a les attributs suivants: Content, user_id. Pour un commentaire, un user_id peut être nul.
Joint:
: join effectue une jointure interne entre deux tables. Donc
va récupérer tous les enregistrements où user_id (de la table des commentaires) est égal à user.id (table des utilisateurs). Ainsi, si vous le faites
Vous obtiendrez un tableau vide comme indiqué.
De plus, join ne charge pas la table jointe en mémoire. Ainsi, si vous le faites
Comme vous le voyez,
comment_1.user.age
lancera à nouveau une requête de base de données en arrière-plan pour obtenir les résultatsComprend:
: includes effectue une jointure externe gauche entre les deux tables. Donc
entraînera une table jointe avec tous les enregistrements de la table des commentaires. Ainsi, si vous le faites
il récupérera les enregistrements où comments.user_id est nul comme indiqué.
De plus, il charge les deux tables en mémoire. Ainsi, si vous le faites
Comme vous pouvez le constater, comment_1.user.age charge simplement le résultat de la mémoire sans lancer une requête de base de données en arrière-plan.
la source
En plus des considérations de performances, il existe également une différence fonctionnelle. Lorsque vous joignez des commentaires, vous demandez des articles qui ont des commentaires - une jointure interne par défaut. Lorsque vous incluez des commentaires, vous demandez tous les articles - une jointure externe.
la source
tl; dr
Je les contraste de deux manières:
jointures - Pour la sélection conditionnelle des enregistrements.
comprend - Lors de l'utilisation d'une association sur chaque membre d'un jeu de résultats.
Version plus longue
Les jointures sont destinées à filtrer l'ensemble de résultats provenant de la base de données. Vous l'utilisez pour effectuer des opérations de définition sur votre table. Considérez cela comme une clause where qui exécute la théorie des ensembles.
Post.joins(:comments)
est le même que
Post.where('id in (select post_id from comments)')
Sauf que s'il y a plus d'un commentaire, vous obtiendrez des articles en double avec les jointures. Mais chaque article sera un article avec des commentaires. Vous pouvez corriger cela avec distinct:
En contrat, la
includes
méthode s'assurera simplement qu'il n'y a pas de requêtes de base de données supplémentaires lors du référencement de la relation (afin que nous ne fassions pas n + 1 requêtes)La morale est, à utiliser
joins
lorsque vous souhaitez effectuer des opérations d'ensemble conditionnelles et à utiliserincludes
lorsque vous allez utiliser une relation sur chaque membre d'une collection.la source
distinct
me fait à chaque fois. Je vous remercie!.joins fonctionne comme une jointure de base de données et joint deux ou plusieurs tables et récupère les données sélectionnées du backend (base de données).
.comprend le travail comme jointure gauche de la base de données. Il a chargé tous les enregistrements du côté gauche, n'a pas la pertinence du modèle du côté droit. Il est utilisé pour un chargement rapide car il charge tous les objets associés en mémoire. Si nous appelons des associations sur le résultat de la requête d'inclusion, cela ne déclenche pas de requête sur la base de données, il renvoie simplement les données de la mémoire car il a déjà chargé des données en mémoire.
la source
'jointures' juste utilisé pour joindre des tables et lorsque vous avez appelé des associations sur des jointures, il lancera à nouveau la requête (cela signifie que de nombreuses requêtes se déclencheront)
le nombre total de SQL est de 11 dans ce cas
Mais avec 'includes', vous souhaiterez charger les associations incluses et les ajouter en mémoire (charger toutes les associations lors du premier chargement) et ne pas lancer de requête à nouveau
lorsque vous obtenez des enregistrements avec des inclus comme @ records = User.includes (: organizations) .where ("organisations.user_id = 1") alors la requête sera
@ records.map {| u | u.organisation.name} aucune requête ne se déclenchera
la source