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?
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").
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
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:
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.
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.
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.
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.
'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
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