Aprenda a Fazer Inner Join & Left Join via LINQ

Aprenda a Fazer Inner Join & Left Join via LINQ

Neste artigo eu vou abordar como fazer junções internas e externas à esquerda com LINQ. Estas junções são conhecidas como inner join e left join, respectivamente.

Uma junção é uma cláusula para selecionar elementos de uma tabela que possuam correspondência em outra(s). A mais comum é conhecida como inner join. Uma condição de igualdade é definida na consulta para retornar os elementos correspondentes nas tabelas.

Obs: Por padrão, o inner join pode ser escrito também omitindo o “inner”. Elementos correspondentes significa linhas de uma tabela que combinam com linhas de outra tabela segundo uma condição.

A notação de conjuntos facilita o entendimento ao passo que concebemos as tabelas como conjuntos e o resultado da junção é a intersecção deles.

SQL INNER JOIN

Figura retirada de: W3C Schools

 

Já o left join retorna todas as colunas que possuam ou não uma correspondência com um elemento da outra tabela. Quando não houver uma correspondência, o resultado será nulo para tal.

É possível realizar a operação de junção não apenas em consultas a uma base de dados SQL, mas em outras fontes de dados, como coleções de objetos (LINQ to Objects), assim como mostro no exemplo a seguir.

Nesse estudo de caso, escolhi contextualizar uma Concessionária que possui registros de carros armazenados em seu pátio. Uma concessionária Toyota, nesse exemplo, só possui registros de carros da mesma marca, e assim por diante. Há também carros que não possuem registro em concessionária alguma.

Primeiramente, instanciamos 5 objetos Concessionária:

Concessionaria csBmw = new Concessionaria() { ConcessionariaId = 1, Localizacao = "Maceió" };
Concessionaria csBmw2 = new Concessionaria() { ConcessionariaId = 2, Localizacao = "Rio de Janeiro" };
Concessionaria csToyota = new Concessionaria() { ConcessionariaId = 3, Localizacao = "São Paulo" };
Concessionaria csFiat = new Concessionaria() { ConcessionariaId = 4, Localizacao = "Curitiba" };
Concessionaria csCitroen = new Concessionaria() { ConcessionariaId = 5, Localizacao = "Salvador" };


Depois, uma lista de objetos Veiculo:

var lstVeiculos = new List<Veiculo>()
{
     new Veiculo(){ Marca = "Fiat", Modelo = "Palio Fire", Ano = DateTime.Today.AddYears(-3), Preco = 17900.0 },
     new Veiculo(){ Marca = "BMW", Modelo = "528i M Sport", Ano = DateTime.Today.AddYears(-1), Preco = 270750.0,                        ConcessionariaId = 2 },
     new Veiculo(){ Marca = "BMW", Modelo = "Série 2 Coupé", Ano = DateTime.Today.AddYears(1), Preco = 190000.0,                        ConcessionariaId = 1 },
     new Veiculo(){ Marca = "Toyota", Modelo = "Etios", Ano = DateTime.Today, Preco = 39950.0, ConcessionariaId = 3 },     
     new Veiculo(){ Marca = "Fiat", Modelo = "Siena", Ano = DateTime.Today.AddYears(-2), Preco = 29990.0 },
     new Veiculo(){ Marca = "Citroen", Modelo = "Citroen C3", Ano = DateTime.Today.AddYears(1), Preco = 41990.0 },
     new Veiculo(){ Marca = "BMW", Modelo = "Série 3 Gran Turismo", Ano = DateTime.Today.AddYears(-6), Preco =                          310000.0, ConcessionariaId = 4 },
     new Veiculo(){ Marca = "Fiat", Modelo = "Mille", Ano = DateTime.Today.AddYears(-9), Preco = 17990.0,                                ConcessionariaId = 5 },
     new Veiculo(){ Marca = "Toyota", Modelo = "Hilux", Ano = DateTime.Today.AddYears(-11), Preco = 57900.0,                            ConcessionariaId = 3 },
     new Veiculo(){ Marca = "Citroen", Modelo = "C4 Picasso", Ano = DateTime.Today.AddYears(-1), Preco = 63990.0 },
     new Veiculo(){ Marca = "Toyota", Modelo = "Novo Corolla", Ano = DateTime.Today.AddYears(1), Preco = 46900.0,                        ConcessionariaId = 1 }
};


E uma lista de Concessionárias:

var lstConcessionarias = new List<Concessionaria>() { csBmw, csBmw2, csToyota, csFiat, csCitroen };


Agora, o inner join retorna os veículos que possuem registro na concessionária. Cada carro pode ter zero ou um registro na concessionária:

Passo a passo:

  1. Definir a fonte de dados 1: lstVeiculos (from veiculo in lstVeiculos…)
  2. A junção: cria-se uma variável para referenciar a fonte de dados 2  (veiculosConcessionaria)
  3. Definir a condição de igualdade da junção: uma boa prática é fazer pelo ID de ambas tabelas/objetos (campo ConcessionariaId)
  4. O foreach imprime o resultado.
var queryVeiculosEmConcessionarias = from veiculo in lstVeiculos
                                     join veiculosConcessionaria in lstConcessionarias
                                     on veiculo.ConcessionariaId equals veiculosConcessionaria.ConcessionariaId
                                     select veiculo;
 
foreach (var veiculo in queryVeiculosEmConcessionarias)
{
    Console.WriteLine(veiculo.Modelo);
}


Por fim, o left join retorna os veículos que possuem registro ou não na concessionária. Quando não houver registro, teremos uma referência nula. Mas, ao utilizar o método DefaultIfEmpty na consulta, previne-se o disparo de uma NullReferenceException, pois este método retorna um valor default se a coleção é vazia. Na projeção (select new…) faço o tratamento da mensagem para quando o modelo do carro não tiver um registro naquela concessionária.

Aqui eu mudei alguns nomes de variáveis.

– “concessionaria” referencia a fonte de dados (lstConcessionarias)

– “veiculosConcessionaria” referencia o resultado da junção (into veiculosConcessionaria). É a partir dessa variável que chamamos o método DefaultIfEmpty para checar se há registros nulos.

var queryVeiculosConcessionarias = from veiculo in lstVeiculos
                                   join concessionaria in lstConcessionarias
                                   on veiculo.ConcessionariaId equals concessionaria.ConcessionariaId                                                    into veiculosConcessionaria
                                   from carros in veiculosConcessionaria.DefaultIfEmpty()
                                   select new
                                   {
                                       Modelo = carros == null ? string.Concat(veiculo.Modelo, " não possui registro                                         na concessionária")
                                         : veiculo.Modelo
                                   };
 
foreach (var veiculo in queryVeiculosConcessionarias)
{
    Console.WriteLine(veiculo.Modelo);
}

Nesse exemplo, eu defini que se houver um registro nulo resultante desse join, a mensagem exibida seria “O carro x não possui registro na concessionária”. É possível fazer essa validação no foreach, mas não bagunce o código. Com mais variáveis no select, teríamos uma miscelânea.

Poderíamos usar o operador null-coalescing (??) para simplificar ainda mais, mas não nesse caso. Este operador é incompatível nessa implementação pois o tipo anônimo “Modelo” é uma string; para usar o coalesce é preciso que ambos os lados do operador sejam do mesmo tipo. Leia mais sobre esse operador aqui.

No SQL Server, os exemplos não diferem muito sintaticamente:

--inner join
 
select Modelo from Veiculo
inner join Concessionaria
on Veiculo.ConcessionariaId = Concessionaria.ConcessionariaId
 
--left join
 
--retorna só os carros que não possuem registro na Concessionária
 
select Modelo from Veiculo
left join Concessionaria
on Veiculo.ConcessionariaId = Concessionaria.ConcessionariaId
where Veiculo.ConcessionariaId is null
 
--OU: retorna todos os carros que possuem ou não registro na Concessionária.
 
--O registro será zero para os carros que não possuem um registro
 
select Modelo, COALESCE(Veiculo.ConcessionariaId, '0') as 'ID da Concessionária'
from Veiculo
left join Concessionaria
on Veiculo.ConcessionariaId = Concessionaria.ConcessionariaId


No meu repositório eu tenho um exemplo completo de como fazer joins com LINQ to SQL. Também anexo o projeto completo aqui.

 

Ajudou? Deixe sua dúvida no comentário.

Até a próxima,

Thiago.



Comentários
  • Otimo artigo e parabéns pelo Contribuição

  • Otimo artigo e parabéns pelo Contribuição

  • Muito Bom

  • Ótimo artigo !

  • Boa!

  • Obrigado por contribuir com o Microsoft TechNet Wiki! Sua contribuição é muito importante para a comunidade e por isso entrarmos em contato apenas para informar sobre as regras de como contribuir com o Portal do TechNet Wiki.

    Pedimos que leia atentamente como proceder para contribuir, e assim evitar que seu artigo, tão importante para à comunidade, seja marcado para remoção devido ao não cumprimento das regras. Veja alguns exemplos:

    - Evitar escrever em primeira pessoa;

    - Evitar expor suas opiniões particulares sobre o assunto;

    - O TechNet Wiki é similar à um Blog, mas não é um Blog Pessoal;

    - Evitar uso apenas de vídeo, importante ter o conteúdo escrito, por isso o portal;

    - Sempre associar sobre tecnologias Microsoft;

    - Usar formatações de acordo as regras de uso;

    - Entre outras regras.

    Por favor, acesse os links e verifique se seu artigo está em acordo às regras.

    social.technet.microsoft.com/.../9460.regras.aspx

    social.technet.microsoft.com/.../9459.como-contribuir.aspx

  • Parabéns.

  • Artigo muito bom! Parabéns!

  • Ótimo artigo!!!! parabéns!!!!

Página 1 de 1 (9 itens)