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