tables rails query multiple left joins inner includes active ruby-on-rails ruby database join rails-activerecord

ruby-on-rails - multiple - rails sql query



Carriles: incluir vs.: unir (8)

''joins'' solo se usa para unir tablas y cuando se llaman asociaciones en uniones, se activará de nuevo la consulta (significa que se activará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)

número total de SQL es 11 en este caso

Pero con ''includes'' cargará ansiosamente 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 obtenga 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} no se activará ninguna consulta

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 hacerlo"

Por lo tanto, el evangelio sobre la extracción de registros asociados que usted sabe que va a usar es usar :include porque obtendrá una combinación y evitará 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)

Está tomando 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 en que puedo obtener una combinación es usar :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 muestro datos de todas ellas. Parece que sería mejor tener una consulta unida en lugar de 6 personas. Sé que en cuanto 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 :include no trabajar como se anuncia.

¿Quizás Rails es consciente del problema de rendimiento y no se une, excepto en ciertos casos?


.joins funciona como unió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 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.


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.


Hace poco 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.

Se une a:

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

recuperará todos los registros donde user_id (de la tabla de comentarios) es igual a user.id (tabla de usuarios). Asi 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. Asi 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 nuevamente en segundo plano para obtener los resultados.

Incluye:

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

resultará en una tabla unida con todos los registros de la tabla de comentarios. Asi 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 comments.user_id sea nil como se muestra.

Además incluye cargas tanto las tablas en la memoria. Asi 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 consulta de base de datos en segundo plano.


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 => users para obtener toda la información del usuario con el fin de ordenar, etc., funcionará bien y tomará menos tiempo que: include, pero digamos 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


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 del blog de Fabio Akita tiene buena información sobre el cambio (consulte la sección titulada "Carga optimizada e impaciente").


.joins solo unirá las tablas y traerá 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á ansiosamente las asociaciones incluidas y las agregará en la memoria. :includes cargas todos los atributos de las tablas incluidas. Si llama a asociaciones en el resultado de la consulta de inclusión, no se activará ninguna consulta.

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


tl; dr

Los contraste de dos maneras:

Uniones - Para 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 (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 distinto:

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

En el contrato, el método de inclusión 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, usar joins cuando desee realizar operaciones de conjuntos condicionales y el uso includes cuando va a utilizar una relación en cada miembro de una colección.