Essa é mais uma pergunta "por que as coisas funcionam dessa maneira" em vez de uma pergunta "não sei como fazer isso" ...
Portanto, o evangelho sobre a obtenção de registros associados que você sabe que usará é o de usar, :include
porque você se unirá e evitará um monte de consultas extras:
Post.all(:include => :comments)
No entanto, quando você olha para os logs, não há associação acontecendo:
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)
Ele está pegando um atalho porque ele puxa todos os comentários de uma vez, mas ainda não é uma junção (que é o que toda a documentação parece dizer). A única maneira de obter uma associação é usar em :joins
vez de :include
:
Post.all(:joins => :comments)
E os logs mostram:
Post Load (6.0ms) SELECT "posts".* FROM "posts"
INNER JOIN "comments" ON "posts".id = "comments".post_id
Estou esquecendo de algo? Eu tenho um aplicativo com meia dúzia de associações e, em uma tela, exibo dados de todas elas. Parece que seria melhor ter uma consulta ingressada em vez de 6 indivíduos. Sei que em termos de desempenho nem sempre é melhor fazer uma junção do que consultas individuais (na verdade, se você estiver gastando o tempo gasto, parece que as duas consultas individuais acima são mais rápidas que a junção), mas depois de todos os documentos Estou lendo, fico surpreso ao ver que :include
não está funcionando como anunciado.
Talvez o Rails esteja ciente do problema de desempenho e não participe, exceto em certos casos?
fonte
includes
(para quem estiver lendo isso) #Respostas:
Parece que a
:include
funcionalidade foi alterada com o Rails 2.1. O Rails costumava fazer a junção em todos os casos, mas por razões de desempenho, foi alterado para usar várias consultas em algumas circunstâncias. Esta postagem no blog de Fabio Akita tem boas informações sobre a alteração (consulte a seção "Carregamento otimizado e ansioso").fonte
.joins
apenas junta as tabelas e traz os campos selecionados em troca. se você chamar associações no resultado da consulta de junções, ele acionará as consultas do banco de dados novamente:includes
ansiosamente carregará as associações incluídas e as adicionará na memória.:includes
carrega todos os atributos de tabelas incluídos. Se você chamar associações no resultado da consulta de inclusão, ela não acionará nenhuma consultafonte
A diferença entre junções e inclusão é que o uso da instrução include gera uma consulta SQL muito maior carregando na memória todos os atributos das outras tabelas.
Por exemplo, se você tiver uma tabela cheia de comentários e usar a: joins => users para extrair todas as informações do usuário para fins de classificação, etc. ela funcionará bem e levará menos tempo que: include, mas diga que deseja exibir o comentário junto com o nome do usuário, email, etc. Para obter as informações usando: joins, será necessário fazer consultas SQL separadas para cada usuário que buscar, enquanto que se você usou: include, essas informações estarão prontas para uso.
Ótimo exemplo:
http://railscasts.com/episodes/181-include-vs-joins
fonte
Recentemente, eu estava lendo mais sobre a diferença entre
:joins
e:includes
nos trilhos. Aqui está uma explicação do que eu entendi (com exemplos :))Considere este cenário:
Um usuário tem muitos comentários e um comentário pertence a um usuário.
O modelo de usuário possui os seguintes atributos: Nome (sequência), Idade (número inteiro). O modelo Comment possui os seguintes atributos: Content, user_id. Para um comentário, um user_id pode ser nulo.
Associa-se:
: joins executa uma junção interna entre duas tabelas. portanto
irá buscar todos os registros em que user_id (da tabela de comentários) seja igual a user.id (tabela de usuários). Assim, se você fizer
Você receberá uma matriz vazia, como mostrado.
Além disso, as junções não carregam a tabela unida na memória. Assim, se você fizer
Como você vê,
comment_1.user.age
disparará uma consulta ao banco de dados novamente em segundo plano para obter os resultadosInclui:
: includes executa uma junção externa esquerda entre as duas tabelas. portanto
resultará em uma tabela unida com todos os registros da tabela de comentários. Assim, se você fizer
ele buscará registros onde comments.user_id é nulo, como mostrado.
Além disso, inclui carrega as duas tabelas na memória. Assim, se você fizer
Como você pode notar, o comment_1.user.age simplesmente carrega o resultado da memória sem disparar uma consulta ao banco de dados em segundo plano.
fonte
Além das considerações de desempenho, também há uma diferença funcional. Ao ingressar nos comentários, você está solicitando postagens que tenham comentários - uma associação interna por padrão. Quando você inclui comentários, solicita todas as postagens - uma associação externa.
fonte
tl; dr
Eu os contraste de duas maneiras:
junções - para seleção condicional de registros.
inclui - Ao usar uma associação em cada membro de um conjunto de resultados.
Versão mais longa
Junções destina-se a filtrar o conjunto de resultados provenientes do banco de dados. Você o usa para fazer operações definidas na sua mesa. Pense nisso como uma cláusula where que executa a teoria dos conjuntos.
Post.joins(:comments)
é o mesmo que
Post.where('id in (select post_id from comments)')
Exceto que, se houver mais de um comentário, você receberá postagens duplicadas com as junções. Mas todas as postagens serão postadas com comentários. Você pode corrigir isso com distintas:
No contrato, o
includes
método simplesmente garante que não haja consultas adicionais ao banco de dados ao fazer referência à relação (para que não façamos n + 1 consultas)A moral é: use
joins
quando quiser executar operações de conjunto condicional e useincludes
quando você estiver usando uma relação em cada membro de uma coleção.fonte
distinct
me pega toda vez. Obrigado!.joins funciona como junção de banco de dados e junta duas ou mais tabelas e busca dados selecionados do back-end (banco de dados).
.inclui o trabalho como junção esquerda do banco de dados. Carregou todos os registros do lado esquerdo, não tem relevância do modelo do lado direito. É usado para carregamento rápido, pois carrega todos os objetos associados na memória. Se chamarmos associações no resultado da consulta de inclusão, ele não acionará uma consulta no banco de dados. Ele simplesmente retorna dados da memória porque já carregou dados na memória.
fonte
'joins' é usado apenas para juntar tabelas e, quando você chama associações em joins, ele aciona novamente a consulta (significa que muitas consultas são acionadas)
número total de SQL é 11 neste caso
Porém, com 'includes', as associações incluídas serão carregadas com entusiasmo e as adicionarão na memória (carregará todas as associações na primeira carga) e não acionará a consulta novamente
Quando você obtém registros com include como @ records = User.includes (: organisations) .where ("organisations.user_id = 1"), a consulta será
@ records.map {| u | u.organisation.name} nenhuma consulta será acionada
fonte