Pregunta

Esto es más como un " por qué las cosas funcionan de esta manera " pregunta en lugar de " No sé cómo hacerlo " pregunta ...

Por lo tanto, el evangelio sobre la extracción de registros asociados que sabes que vas a usar es utilizar : include porque obtendrás una combinación y evitarás un montón de consultas adicionales:

Post.all(:include => :comments)

Sin embargo, cuando miras los registros, no ocurre 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) 

Es es tomar un atajo porque extrae todos los comentarios a la vez, pero aún no es una combinación (que es lo que parece decir toda la documentación). La única forma de obtener una combinación es utilizar : 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 conjunta en lugar de 6 personas. Sé que en lo que respecta al rendimiento, no siempre es mejor hacer una combinación en lugar de consultas individuales (de hecho, si pasa el tiempo invertido, 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. 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.

¿Fue útil?

Solución

Parece que la funcionalidad : include se cambió con Rails 2.1. Rails solía hacer la unión en todos los casos, pero por razones de rendimiento, se modificó para usar varias consultas en algunas circunstancias. Esta publicación de blog por Fabio Akita tiene buena información sobre el cambio (consulte la sección titulada "Carga optimizada e impaciente").

Otros consejos

.joins solo se unirá a las tablas y mostrará los campos seleccionados a cambio. Si llama a las asociaciones en el resultado de la consulta de combinaciones, volverá a iniciar las consultas de la base de datos

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

La diferencia entre las uniones y la 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 la (s) otra (s) tabla (s).

Por ejemplo, si tiene una tabla llena de comentarios y usa a: joins = > para que los usuarios obtengan toda la información del usuario con fines de clasificación, etc. funcionará bien y tomará menos tiempo que: incluir, pero supongamos que desea mostrar el comentario junto con el nombre de los usuarios, el correo electrónico, etc. Para obtener la información mediante: uniones , tendrá que realizar consultas SQL separadas para cada usuario que obtenga, mientras que si utiliza: incluir esta información está lista para su uso.

Gran ejemplo:

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

Además de las consideraciones de rendimiento, también hay una diferencia funcional. Cuando te unes a los comentarios, estás solicitando publicaciones que tienen comentarios, una combinación interna de forma predeterminada. Cuando incluye comentarios, solicita todas las publicaciones: una combinación externa.

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

Joins:

: uniones 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">]>

recuperará 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")

#=> <ActiveRecord::Relation []>

Obtendrá una matriz vacía como se muestra.

Además, las combinaciones no cargan la tabla combinada 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 puede ver, comment_1.user.age iniciará una consulta de base de datos nuevamente en segundo plano para obtener los resultados

Incluye :

: incluye realiza una combinació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. Por lo tanto, 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">]>

recuperará los registros donde comments.user_id sea nulo, como se muestra.

Además incluye cargas tanto 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 disparar una base de datos en segundo plano.

tl;dr

Los comparo de dos maneras:

combinaciones : 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 tiene la intención de filtrar el conjunto de resultados provenientes de la base de datos. Lo usas para establecer operaciones en tu mesa. Piense en esto como una cláusula donde se realiza la teoría de conjuntos.

Post.joins(:comments)

es lo mismo que

Post.where ('id in (seleccione 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 distinto:

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

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

.joins funciona como una combinación de base de datos y une dos o más tablas y recupera datos seleccionados del backend (base de datos).

.incluye el trabajo como una combinación a la 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 entusiasmo porque carga todos los objetos asociados en la memoria. Si llamamos asociaciones en el resultado de la consulta de inclusión, entonces no se activa una consulta en la base de datos, simplemente devuelve datos de la memoria porque ya ha cargado datos en la memoria.

'combinaciones' solo se usaron para unir tablas y cuando llamaste a asociaciones en combinaciones, nuevamente se activará la consulta (significa que muchas consultas se activará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á las asociaciones incluidas y las agregará en la memoria (cargará todas las asociaciones en la primera carga) y no volverá a iniciar la consulta

cuando obtienes registros con incluye como @ records = User.includes (: Organizations) .where (" organisations.user_id = 1 ") entonces 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}     ninguna consulta se activará

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top