Rieles: incluir vs.

Esta es más una pregunta de “por qué las cosas funcionan así” en lugar de una pregunta de “no sé cómo hacer esto” …

Entonces el evangelio de tirar de los registros asociados que sabes que vas a usar es usar :include porque obtendrás una combinación y evitar un montón de consultas adicionales:

 Post.all(:include => :comments) 

Sin embargo, cuando mira los registros, no hay ninguna unión que suceda:

 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 toda la documentación parece decir). La única forma en que puedo obtener una unión es usar :joins join 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 visualizo datos de todas ellas. Parece que sería mejor tener una consulta conjunta en lugar de 6 personas. Sé que en lo que respecta al rendimiento, no siempre es mejor hacer una unión en lugar de consultas individuales (de hecho, si vas por tiempo pasado, parece que las dos consultas individuales anteriores son más rápidas que la combinación), pero después de todos los documentos He estado leyendo Estoy sorprendido de ver :include no funciona según lo anunciado.

¿Tal vez Rails es consciente del problema de rendimiento y no se une, excepto en ciertos casos?

Parece que la funcionalidad :include se cambió con Rails 2.1. Rails solía hacer la unión en todos los casos, pero por motivos de rendimiento se cambió para utilizar consultas múltiples en algunas circunstancias. Esta publicación del blog de Fabio Akita contiene buena información sobre el cambio (consulte la sección titulada “Carga optimizada optimizada”).

.joins simplemente se une a las tablas y trae los campos seleccionados a cambio. si llama a asociaciones en el resultado de la consulta de uniones, activará las consultas de la base de datos de nuevo

:includes cargará las asociaciones incluidas y las agregará en la memoria. :includes cargas de todos los atributos de tablas incluidos. Si llama a las asociaciones en el resultado de la consulta de inclusión, no activará ninguna consulta

Mi blog tiene una explicación detallada de las diferencias

La diferencia entre join e include es que el uso de la instrucción include genera una consulta SQL mucho más grande al cargar en la memoria todos los atributos de la (s) otra (s) tabla (s).

Por ejemplo, si tiene una tabla llena de comentarios y usa un: join => users para obtener toda la información del usuario para fines de clasificación, etc. funcionará bien y tomará menos tiempo que: include, pero digamos que desea mostrar el comentario junto con el nombre del usuario, correo electrónico, etc. Para obtener la información mediante: join, tendrá que realizar consultas SQL por separado para cada usuario que recupere, mientras que si la usó: include esta información está lista para usar.

Gran ejemplo:

http://railscasts.com/episodes/181-include-vs-joins

Además de las consideraciones de rendimiento, también hay una diferencia funcional. Cuando unes comentarios, estás solicitando publicaciones que tengan comentarios, una unión interna por defecto. Cuando incluye comentarios, está solicitando todas las publicaciones: una combinación externa.

Hace poco estuve leyendo más sobre la diferencia entre :joins y :includes en Rails. Aquí hay una explicación de lo que entendí (con ejemplos :))

Considera 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 de comentario tiene los siguientes atributos: Contenido, user_id. Para un comentario, un user_id puede ser nulo.

Se une:

: une realiza una unión interna entre dos tablas. Así

 Comment.joins(:user) #=> , #, #]> 

obtendrá todos los registros donde user_id (de la tabla de comentarios) es igual a user.id (tabla de usuarios). Por lo tanto, si lo haces

 Comment.joins(:user).where("comments.user_id is null") #=>  

Obtendrá una matriz vacía como se muestra.

Además, une no carga la tabla unida en la memoria. Por lo tanto, si lo haces

 comment_1 = Comment.joins(:user).first comment_1.user.age #=>←[1m←[36mUser Load (0.0ms)←[0m ←[1mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1←[0m [["id", 1]] #=> 24 

Como ve, comment_1.user.age disparará una consulta de base de datos nuevamente en el fondo para obtener los resultados

Incluye:

: includes realiza una combinación externa izquierda entre las dos tablas. Así

 Comment.includes(:user) #=>, #, #, #]> 

dará como resultado una tabla unida con todos los registros de la tabla de comentarios. Por lo tanto, si lo haces

 Comment.includes(:user).where("comment.user_id is null") #=> #]> 

obtendrá registros donde comments.user_id es nil como se muestra.

Además, incluye cargas tanto de las tablas en la memoria. Por lo tanto, si lo haces

 comment_1 = Comment.includes(:user).first comment_1.user.age #=> 24 

Como puede observar, comment_1.user.age simplemente carga el resultado de la memoria sin activar una consulta de base de datos en segundo plano.

.joins funciona como una combinación de base de datos y une dos o más tablas y recupera los datos seleccionados desde el back-end (base de datos).

.incluye el trabajo como parte de la base de datos de la izquierda. Cargó todos los registros del lado izquierdo, no tiene relevancia del modelo del lado derecho. Está acostumbrado a una carga ansiosa porque carga todos los objetos asociados en la memoria. Si llamamos a las 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 han cargado datos en la memoria.

tl; dr

Los contraste de dos maneras:

se une – Para la selección condicional de registros.

includes – Cuando se usa una asociación en cada miembro de un conjunto de resultados.

Versión más larga

Joins está destinado a filtrar el conjunto de resultados que proviene de la base de datos. Lo usas para hacer operaciones de conjunto en tu mesa. 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, obtendrás publicaciones duplicadas con las uniones. Pero cada publicación será una publicación que tenga comentarios. Puedes corregir esto con distinct:

 Post.joins(:comments).count => 10 Post.joins(:comments).distinct.count => 2 

En el contrato, el método includes 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 n + 1 consultas)

 Post.includes(:comments).count => 4 # includes posts without comments so the count might be higher. 

La moraleja es, el uso se joins cuando se desea realizar operaciones de conjunto condicionales y el uso includes cuando se va a utilizar una relación en cada miembro de una colección.

‘join’ solo se utiliza para unir tablas y cuando llamas asociaciones en uniones, activará la consulta nuevamente (significa que se generarán muchas consultas)

 lets suppose you have tow model, User and Organisation User has_many organisations suppose you have 10 organisation for a user @records= User.joins(:organisations).where("organisations.user_id = 1") QUERY will be select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1 it will return all records of organisation related to user and @records.map{|u|u.organisation.name} it run QUERY like select * from organisations where organisations.id = x then time(hwo many organisation you have) 

el número total de SQL es 11 en este caso

Pero con ‘includes’ cargará con entusiasmo las asociaciones incluidas y las agregará en la memoria (cargará todas las asociaciones en la primera carga) y no activará la consulta nuevamente

cuando obtiene registros con includes como @ records = User.includes (: organizations) .where (“organisations.user_id = 1”), la consulta será

 select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1 and select * from organisations where organisations.id IN(IDS of organisation(1, to 10)) if 10 organisation and when you run this 

@ records.map {| u | u.organisation.name} no se lanzará ninguna consulta