Rieles: incluir frente a: unir
Esta es más una pregunta de "por qué las cosas funcionan de esta manera" que una pregunta de "No sé cómo hacer esto"...
Entonces, la mejor manera de extraer registros asociados que sabes que vas a usar es usarlos :include
porque obtendrás una unión y evitarás un montón de consultas adicionales:
Post.all(:include => :comments)
Sin embargo, cuando miras los registros, no se produce ninguna unión:
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)
Está tomando un atajo porque extrae todos los comentarios a la vez, pero todavía no es una unión (que es lo que parece decir toda la documentación). La única forma en que puedo unirme es usar :joins
en lugar de :include
:
Post.all(:joins => :comments)
Y los registros muestran:
Post Load (6.0ms) SELECT "posts".* FROM "posts"
INNER JOIN "comments" ON "posts".id = "comments".post_id
¿Me estoy perdiendo de algo? Tengo una aplicación con media docena de asociaciones y en una pantalla muestro datos de todas ellas. Parece que sería mejor tener una consulta unida en lugar de 6 personas. Sé que en términos de rendimiento no siempre es mejor realizar una unión en lugar de consultas individuales (de hecho, si nos fijamos en el tiempo invertido, parece que las dos consultas individuales anteriores son más rápidas que la unión), pero después de todos los documentos He estado leyendo y me sorprende ver que :include
no funciona como se anuncia.
¿Quizás Rails es consciente del problema de rendimiento y no se une excepto en ciertos casos?
Parece que la :include
funcionalidad se cambió con Rails 2.1. Rails solía realizar la unión en todos los casos, pero por razones de rendimiento se cambió para usar múltiples consultas en algunas circunstancias. Esta publicación de blog de Fabio Akita tiene buena información sobre el cambio (consulte la sección titulada "Carga ansiosa optimizada").
.joins
simplemente unirá las tablas y traerá los campos seleccionados a cambio. si llama a asociaciones en el resultado de la consulta de uniones, se activarán consultas de la base de datos nuevamente
:includes
cargará ansiosamente las asociaciones incluidas y las agregará a la memoria. :includes
carga todos los atributos de las tablas incluidas. Si llama a asociaciones sobre el resultado de la consulta de inclusión, no se activará ninguna consulta.
La diferencia entre uniones e inclusión es que el uso de la declaración de inclusión genera una consulta SQL mucho más grande que carga en la memoria todos los atributos de las otras tablas.
Por ejemplo, si tiene una tabla llena de comentarios y utiliza :joins => usuarios para obtener toda la información del usuario con fines de clasificación, etc., funcionará bien y tomará menos tiempo que :include, pero diga que desea mostrar el comentario junto con el nombre del usuario, correo electrónico, etc. Para obtener la información usando :joins, tendrá que realizar consultas SQL separadas para cada usuario que obtenga, mientras que si usó :include esta información está lista para usar.
Gran ejemplo:
http://railscasts.com/episodes/181-include-vs-joins