Les jointures SQL en Rails 4

Imaginons que votre but est de récupérer tous les résultats dont l’id est la clé étrangère des lignes d’une autre table si ces dernières contiennent une valeur donnée.

La première solution consiste à utiliser le select de Ruby (retourne un tableau d’éléments Active Record) :

La seconde est de le faire en SQL (retourne un tableau d’éléments Active Record) :

La troisième solution est d’utiliser la méthode joins (retourne un objet Active Relation) :

Cependant, les résultats retournés par la méthode joins sont chargés en lecture seule et sans leurs relations, ce qui peut poser problème.

Cette jointure SQL est de type Inner JOIN : il s’agit d’une intersection entre les tables comparées, seules les lignes strictement en commun seront retournées. La raison pour laquelle les résultats sont marqués readonly vient de Rails : les « piggy-back attributes » sont, d’après ce topic StackOverFlow, les attributs de la seconde table qui sont stockés dans les objets retournés. Ces objets ne représentent pas le modèle et ne peuvent donc pas être sauvegardés.

Le comportement de la méthode joins peut être modifié en passant des paramètres, pour obtenir n’importe quel type de jointure (doc). Il est fréquent que cette méthode retourne des doublons dans une relation belongs_to_has_many, veillez à chainer la méthode uniq si nécessaire.

Il existe d’autre part la méthode includes qui s’utilise exactement comme joins (retourne un objet Active Relation) :

À la différence de joins, cette méthode charge toutes les associations des objets demandés : cette technique s’appelle l’eager loading et peut s’optimiser en utilisant includes correctement (doc).

La « stratégie » des requêtes qui est implémentée dans cette méthode varie : elle peut soit effectuer plusieurs requêtes soit un LEFT OUTER JOIN.

La documentation alerte sur l’effet potentiellement inattendu de cette méthode, du fait de ce type de jointure SQL. Elle recommande d’écrire sa propre requête à l’aide de la méthode joins : voyons comment faire.

Dans l’exemple plus haut, nous avions une requête retournant des résultats corrects, sans eager loading, mais contenant beaucoup trop de champs pour représenter notre modèle. Précisons-les :

Les résultats de cette requête seront désormais enregistrables, mais l’utilisation de liaisons déclenchera de nouvelles requêtes. Il est bien sûr possible de placer cette requête dans un scope.

L’eager loading n’est pas toujours une bonne solution, car le coût de récupérer la totalité d’une table peut s’avérer moins efficace que de récupérer la poignée de résultats dont vous avez besoin. Cela ne semble toutefois pas possible avec la méthode joins.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.