Esta es más una pregunta de "por qué las cosas funcionan de esta manera" en lugar de una pregunta de "No sé cómo hacer esto" ...
Entonces, el evangelio sobre extraer los registros asociados que sabe que va a usar es :include
porque obtendrá una combinación y evitará un montón de consultas adicionales:
Post.all(:include => :comments)
Sin embargo, cuando nos fijamos en los registros, no se produce una 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)
Se está tomando un atajo porque tira todos los comentarios a la vez, pero todavía no es una unión (que es lo que toda la documentación parece decir). La única forma en que puedo obtener una combinación es usar en :joins
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 visualizo datos de todas ellas. Parece que sería mejor tener una consulta unida en lugar de 6 individuos. Sé que, en términos de rendimiento, no siempre es mejor hacer una unión en lugar de consultas individuales (de hecho, si va por 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, 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?
fuente
includes
(para cualquiera que lea esto)Respuestas:
Parece que la
:include
funcionalidad se cambió con Rails 2.1. Rails solía hacer 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 optimizada optimizada").fuente
.joins
solo se unirá a las tablas y traerá los campos seleccionados a cambio. si llama a asociaciones en el resultado de la consulta de combinaciones, volverá a generar consultas en la base de datos:includes
ansioso cargará las asociaciones incluidas y las agregará a la memoria.:includes
carga todos los atributos de tablas incluidos. Si llama a asociaciones en el resultado de la consulta de inclusión, no activará ninguna consultafuente
La diferencia entre unir e incluir 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 usa un: une => usuarios para obtener toda la información del usuario para clasificar, etc. funcionará bien y tomará menos tiempo que: incluir, pero digamos que desea mostrar el comentario junto con el nombre de usuario, correo electrónico, etc. Para obtener la información usando: une, tendrá que hacer consultas SQL por separado para cada usuario que busque, mientras que si usó: incluir esta información está lista para usar.
Gran ejemplo:
http://railscasts.com/episodes/181-include-vs-joins
fuente
Recientemente estuve leyendo más sobre la diferencia entre
:joins
y:includes
en los rieles. Aquí hay una explicación de lo que entendí (con ejemplos :))Considere este escenario:
Un usuario tiene muchos comentarios y un comentario pertenece a un usuario.
El modelo de usuario tiene los siguientes atributos: Nombre (cadena), Edad (entero). El modelo Comentario tiene los siguientes atributos: Contenido, id_usuario. Para un comentario, un user_id puede ser nulo.
Uniones:
: une realiza una unión interna entre dos tablas. Así
buscará todos los registros donde user_id (de la tabla de comentarios) sea igual a user.id (tabla de usuarios). Entonces si lo haces
Obtendrá una matriz vacía como se muestra.
Además, las uniones no cargan la tabla unida en la memoria. Entonces si lo haces
Como puede ver,
comment_1.user.age
activará nuevamente una consulta de base de datos en segundo plano para obtener los resultadosIncluye:
: Incluye realiza una unión externa izquierda entre las dos tablas. Así
dará como resultado una tabla unida con todos los registros de la tabla de comentarios. Entonces si lo haces
buscará registros donde comments.user_id es nulo como se muestra.
Además incluye cargas tanto las tablas en la memoria. Entonces si lo haces
Como puede observar, comment_1.user.age simplemente carga el resultado de la memoria sin disparar una consulta de base de datos en segundo plano.
fuente
Además de las consideraciones de rendimiento, también hay una diferencia funcional. Cuando une comentarios, está solicitando publicaciones que tengan comentarios, una unión interna de forma predeterminada. Cuando incluye comentarios, solicita todas las publicaciones: una combinación externa.
fuente
tl; dr
Los comparo de dos maneras:
une : para la selección condicional de registros.
incluye : cuando se utiliza una asociación en cada miembro de un conjunto de resultados.
Versión más larga
Uniones está destinado a filtrar el conjunto de resultados proveniente de la base de datos. Lo usa para realizar operaciones de configuración en su tabla. Piense en esto como una cláusula where que realiza la teoría de conjuntos.
Post.joins(:comments)
es lo mismo que
Post.where('id in (select post_id from comments)')
Excepto que si hay más de un comentario, recibirá publicaciones duplicadas con las uniones. Pero cada publicación será una publicación que tenga comentarios. Puede corregir esto con distintos:
En el contrato, el
includes
método simplemente se asegurará de que no haya consultas adicionales de la base de datos al hacer referencia a la relación (para que no hagamos consultas n + 1)La moraleja es, úsela
joins
cuando desee realizar operaciones de conjuntos condicionales y úselaincludes
cuando vaya a utilizar una relación en cada miembro de una colección.fuente
distinct
me atrapa todo el tiempo. ¡Gracias!.joins funciona como unión de base de datos y une dos o más tablas y obtiene datos seleccionados del backend (base de datos).
.incluye el trabajo como combinación izquierda de la base de datos. Cargó todos los registros del lado izquierdo, no tiene relevancia del modelo del lado derecho. Se utiliza para cargar con impaciencia porque carga todos los objetos asociados en la memoria. Si llamamos a asociaciones en el resultado de la consulta de inclusión, entonces no activa una consulta en la base de datos, simplemente devuelve datos de la memoria porque ya ha cargado datos en la memoria.
fuente
'Uniones' solo se usaba para unir tablas y cuando llamaste a asociaciones en uniones, volverá a activar la consulta (significa que se activarán muchas consultas)
el número total de SQL es 11 en este caso
Pero con 'incluye' ansioso cargará las asociaciones incluidas y las agregará en la memoria (cargará todas las asociaciones en la primera carga) y no activará nuevamente la consulta
cuando obtiene registros con incluye como @ records = User.includes (: organizaciones) .where ("organisations.user_id = 1") entonces la consulta será
@ records.map {| u | u.organisation.name} no se activará ninguna consulta
fuente