💧🔎⏳ Elixir: Qual a diferença de usar só preload e preload com join?
Tags: elixir, braziliandevs, ecto, database
Published: 2022-05-12 00:30:00

Vamos usar o projeto do post [Sintaxes de consulta em Elixir: pipe X palavras-chave](https://dev.to/maiquitome/sintaxes-de-consulta-em-elixir-pipe-x-palavras-chave-535n) para fazermos uns testes com os comentários e observar o comportamento de usar **só preload** e **preload com join**.

[![A Jornada do Autodidata em Inglês](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkoqkmh9hic17tnddjf6.png)](https://go.hotmart.com/R93865620U)

Vamos buscar os posts com seus comentários:

# Exemplo só com preload

```elixir
from(p in Post, preload: [:comments]) |> Repo.all()
```

ou

```elixir
Post
|> preload([:comments])
|> Repo.all
```

resultado:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yqthgguutweuf8u1ga5z.png)
 
O exemplo acima buscou todas as postagens do banco de dados e, em seguida, fez uma __consulta separada__ retornando todos os comentários associados às postagens fornecidas. Os comentários foram processados ​​e associados a cada post sendo retornados no campo `comments`.

Pontos a observar:
1. Nenhum registro apareceu duplicado.
2. Todos os posts apareceram, mesmo os posts sem comentários.


# Exemplo com preload e join

Muitas vezes, você pode querer que postagens e comentários sejam selecionados e filtrados na mesma consulta. Para esses casos, você pode dizer explicitamente que uma junção existente seja pré-carregada no conjunto de resultados:


```elixir
Repo.all(
      from p in Post,
        join: c in assoc(p, :comments),
        preload: [comments: c]
    )
```

ou

```elixir
Post
|> join(:inner, [p], c in assoc(p, :comments))
|> preload([:comments])
|> Repo.all()

# ou

Post
|> join(:inner, [p], c in Comment, on: p.id == c.post_id)
|> preload([:comments])
|> Repo.all()
```

resultado:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l9dgit6rtlswpzz7enyn.png)
  
resultado similar a uma query usando SQL:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oxmeqxxc0yagc628or9j.png)
 
Pontos a observar:
1. Como o post de id 1 tinha dois comentários, ele apareceu duplicado, similar a consulta com SQL.
2. Apareceram apenas os posts que possuem comentários.

Podemos corrigir a duplicação usando o `distinct`:

```elixir
Repo.all(
      from p in Post,
        distinct: true,
        join: c in assoc(p, :comments),
        preload: [comments: c]
    )
```
ou
```elixir
Post
|> distinct(true)
|> join(:inner, [p], c in assoc(p, :comments))
|> preload([:comments])
|> Repo.all()
```

resultado:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otlidf1psye46qf50ddj.png)

Agora temos apenas os posts que possuem comentários.

# Selecionando os campos do preload

Para melhorar o nosso desempenho, ao invés de trazer todos os campos do comentário, podemos selecionar os campos que queremos:

```elixir
comments_query =
      Comment
      |> select([c], c.body)

    Post
    |> distinct(true)
    |> join(:inner, [p], c in assoc(p, :comments))
    |> preload(comments: ^comments_query)
    |> Repo.all()
```

resultado:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4u8ovbhiaiy530zmls7v.png)
 

# Conclusão

O **preload sem join** buscará todas as postagens do banco de dados e, em seguida, fará uma __consulta separada__ retornando todos os comentários associados às postagens fornecidas. Já usando **preload com join** em vez de emitir uma consulta separada para buscar comentários, o Ecto buscará postagens e comentários em uma __única consulta__ e, em seguida, fará uma passagem separada associando cada comentário à postagem pai.