Rails:incluir vs:une


Esta es más una pregunta de "por qué funcionan las cosas de esta manera" en lugar de una pregunta de "No se cómo hacer esto"...

Así que el evangelio sobre la extracción de registros asociados que sabes que vas a usar es usar :include porque obtendrás una unión y evitarás un montón de consultas adicionales:

Post.all(:include => :comments)

Sin embargo, cuando usted mira los registros, no hay join sucediendo:

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) 

It está tomando un atajo porque extrae todos los comentarios a la vez, pero todavía no es un join (que es lo que parece decir toda la documentación). La única forma en que puedo obtener un join 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 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 join-ed en lugar de 6 personas. Sé que en cuanto al rendimiento, no siempre es mejor hacer un join en lugar de consultas individuales (de hecho, si vas por el tiempo empleado, parece que las dos consultas individuales anteriores son más rápidas que la unión), pero después de todos los documentos que he estado leyendo me sorprende ver :include no funciona como se anuncia.

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

Author: Simone Carletti, 2009-07-30

8 answers

Parece que la funcionalidad :include se cambió con Rails 2.1. Rails solía hacer la combinación en todos los casos, pero por razones de rendimiento se cambió para usar varias consultas en algunas circunstancias. Esta entrada de blog de Fabio Akita tiene buena información sobre el cambio (ver la sección titulada "Optimized Eager Loading").

 167
Author: Greg Campbell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-10 19:26:59

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

:includes will eager cargar las asociaciones incluidas y añadirlas en la memoria. :includes carga todos los atributos de las tablas incluidas. Si llama a las asociaciones en el resultado de la consulta include, no activará ninguna consulta

Mi entrada de blog tiene una explicación detallada de las diferencias

 79
Author: Prem,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-26 19:13:18

La diferencia entre joins e include es que el uso de la instrucción include genera una consulta SQL mucho más grande cargando en la memoria todos los atributos de las otras tablas.

Por ejemplo, si tiene una tabla llena de comentarios y utiliza un :joins => users 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 hacer consultas SQL separadas para cada usuario que obtenga, mientras que si utiliza :include esta información está lista para su uso.

Gran ejemplo:

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

 67
Author: holden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-29 09:20:40

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

 50
Author: Brian Maltzan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-30 16:12:08

Recientemente estuve leyendo más sobre la diferencia entre :joins y :includes en rails. He aquí 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 User tiene los siguientes atributos: Name (string), Age(integer). El modelo de comentarios tiene los siguientes atributos: Content, user_id. Para un comentario un user_id puede ser nulo.

Joins:

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

Comment.joins(:user)

#=> <ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first   comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">, 
     #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,    
     #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">]>

Obtendrá todos los registros donde user_id (de la tabla de comentarios) es igual a user.id (tabla de usuarios). Así que si lo haces

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

#=> <ActiveRecord::Relation []>

Obtendrá una matriz vacía como se muestra.

Además, joins no carga la tabla unida en la memoria. Así que 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 puede ver, comment_1.user.age activará una consulta de base de datos de nuevo en segundo plano para obtener los resultados

Incluye:

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

Comment.includes(:user)

#=><ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">,
   #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,
   #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">,    
   #<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

Dará como resultado una tabla unida con todos los registros de la tabla de comentarios. Así que si lo haces

Comment.includes(:user).where("comment.user_id is null")
#=> #<ActiveRecord::Relation [#<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

Buscará registros donde los comentarios.user_id es nil como se muestra.

Además incluye cargas tanto de las tablas en la memoria. Así que si lo haces

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

Como puedes notar comment_1.usuario.la edad simplemente carga el resultado de la memoria sin disparar una consulta de base de datos en segundo plano.

 45
Author: Aaditi Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-01 08:21:40

tl; dr

Los contraste de dos maneras:{[10]]}

Joins - Para la selección condicional de registros.

Includes - Cuando se utiliza 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. Usted lo utiliza para hacer operaciones de conjunto en su mesa. Piense en esto como una cláusula where que realiza la teoría de conjuntos.

Post.joins(:comments)

Es lo mismo as

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 post será un post que tiene comentarios. Puedes corregir esto con distinct:

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

En contract, el método includes simplemente se asegurará de que no haya consultas de base de datos adicionales 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, use joins cuando quiera hacer operaciones de conjuntos condicionales y use includes cuando vas a usar una relación en cada miembro de una colección.

 5
Author: Kevin Choubacha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-04 23:58:18

.joins funciona como database join y une dos o más tablas y obtiene los datos seleccionados del backend(base de datos).

.incluye el trabajo como unió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 la carga ansiosa porque carga todos los objetos asociados en la memoria. Si llamamos a las asociaciones en el resultado de la consulta include, entonces no dispara una consulta en la base de datos, simplemente devuelve datos de la memoria porque ya ha cargado datos en memoria.

 4
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-19 14:22:57

'joins' solo se usa para unir tablas y cuando se llama a asociaciones en joins, entonces volverá a disparar query (significa que muchas query se dispararán)

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á ansiosamente las asociaciones incluidas y las agregará en memoria(cargará todas las asociaciones en la primera carga) y no disparará la consulta nuevamente

Cuando obtienes registros con includes like @ registros = Usuario.incluye (: organizaciones).donde ("organizaciones.user_id = 1") entonces 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 query will fire

 0
Author: Thorin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-24 08:31:30