Share via


Cláusula join (Referência de C#)

O join cláusula é útil para associar elementos de seqüências de origem diferentes que não têm nenhum relacionamento direto no modelo de objeto. O único requisito é que os elementos em cada fonte compartilham algum valor que pode ser comparado a igualdade. Por exemplo, um distribuidor de alimentos pode ter uma lista de fornecedores de um determinado produto e uma lista de compradores. A join cláusula pode ser usada, por exemplo, para criar uma lista de fornecedores e compradores desse produto que estejam todos na mesma região de especificado.

A join cláusula utiliza duas seqüências de origem como entrada. Os elementos em cada seqüência devem ser ou conter uma propriedade que pode ser comparada a uma propriedade correspondente na seqüência de. O join cláusula compara chaves especificadas de igualdade, usando a sintaxe especial equals palavra-chave. Todas as associações realizadas pelo join cláusula são equijoins. A forma da saída de um join cláusula varia de acordo com o tipo específico de associação que você está executando. Estes são os três tipos mais comuns de associação:

  • Associação interna

  • Associação de grupo

  • Associação externa esquerda

Associação interna

O exemplo a seguir mostra um simple equijoin interna. Esta consulta produz uma simples seqüência de "nome do produto / categoria" pares. A mesma seqüência de categoria será exibido em vários elementos. Se um elemento de categories não tem correspondente products, essa categoria não aparecerão nos resultados.

var innerJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID
    select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence

Para obter mais informações, consulte Como executar junções internas (Guia de Programação em C#).

Group Join

A join cláusula com um into expressão denomina-se uma associação de grupo.

var innerGroupJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    select new { CategoryName = category.Name, Products = prodGroup };

Uma associação de grupo produz uma seqüência de resultados hierárquicos, que associa elementos na seqüência de origem à esquerda com um ou mais elementos correspondentes a seqüência de origem do lado direito. Uma associação de grupo não possui nenhum equivalente em termos de relacionais; ele é essencialmente uma seqüência de matrizes de objeto.

Se nenhum elemento da seqüência de origem direita encontram-se para corresponder a um elemento na fonte à esquerda, a join cláusula produzirá uma matriz vazia para aquele item. Portanto, a associação de grupo ainda basicamente é uma equijoin de interna, exceto que a seqüência de resultado é organizada em grupos.

Se você acabou de selecionar os resultados de uma associação de grupo, você pode acessar os itens, mas você não pode identificar a chave que eles coincidem em. Portanto, é geralmente mais úteis selecionar os resultados da associação de grupo em um novo tipo também tem o nome da chave, conforme mostrado no exemplo anterior.

Além disso, você pode usar claro, o resultado de uma associação de grupo como gerador de outra subconsulta:

var innerGroupJoinQuery2 =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from prod2 in prodGroup
    where prod2.UnitPrice > 2.50M
    select prod2;

Para obter mais informações, consulte Como executar junções agrupadas (Guia de Programação em C#).

Junção externa esquerda

Em uma junção externa esquerda, todos os elementos na seqüência de origem à esquerda são retornados, mesmo se nenhum elemento correspondente na seqüência correta. Para executar uma associação externa esquerda na LINQ, use o DefaultIfEmpty método em combinação com uma associação de grupo para especificar um elemento do lado direito padrão para produzir se um elemento do lado esquerdo não tem nenhuma correspondência. Você pode usar null como o valor padrão para qualquer referência tipo, ou você pode especificar um tipo definido pelo usuário padrão. No exemplo a seguir, um tipo definido pelo usuário padrão é mostrado:

var leftOuterJoinQuery =
    from category in categories
    join prod in products on category.ID equals prod.CategoryID into prodGroup
    from item in prodGroup.DefaultIfEmpty(new Product { Name = String.Empty, CategoryID = 0 })
    select new { CatName = category.Name, ProdName = item.Name };

Para obter mais informações, consulte Como executar junções externas esquerdas (Guia de Programação em C#).

O operador equals

A join cláusula realiza uma equijoin. Em outras palavras, você pode apenas base correspondentes em termos de igualdade de duas chaves. Não há suporte para outros tipos de comparações, como "maior que" ou "não iguais". Para tornar claro que todas as associações são equijoins, o join cláusula usa a equals palavra-chave em vez da == operador. O equals palavra-chave só pode ser usado em um join cláusula e ele difere do == operador em um aspecto importante. Com equals, a tecla esquerda consome a seqüência de origem externa e a tecla direita consome a fonte interna. A fonte externa é somente no escopo no lado esquerdo da equals e a seqüência de origem interna é somente no escopo no lado direito.

Não Equijoins

Você pode executar o não-equijoins, troca de associações e outras operações de associação personalizado, uso de várias from cláusulas para introduzir novas seqüências de forma independente em uma consulta. Para obter mais informações, consulte Como executar operações de junção personalizadas (Guia de Programação em C#).

Ingressa no vs de coleções do objeto.tabelas relacionais

Em um LINQ expressão de consulta, as operações são realizadas em coleções de objeto de associação. Coleções de objetos não podem ser "associadas" exatamente da mesma forma como duas tabelas relacionais. Na LINQ, explícita join cláusulas só são necessários quando duas seqüências de origem não estão ligadas por qualquer relação. Ao trabalhar com LINQ to SQL, tabelas de chaves externas são representadas no modelo de objeto como propriedades da tabela primária. Por exemplo, no banco de dados Northwind, a tabela Customer tem uma relação de chave externa com a tabela Pedidos. Quando você mapeia as tabelas ao modelo de objeto, a classe Customer tem uma propriedade de pedidos que contém a coleção de ordens associadas a esse cliente. Na verdade, a associação já foi feita para você.

Para obter mais informações sobre como fazer consultas em tabelas relacionadas no contexto de LINQ to SQL, consulte Como mapear relações de banco de dados.

Chaves compostas

Você pode testar a igualdade de vários valores usando uma chave composta. Para obter mais informações, consulte Como unir usando chaves compostas (Guia de Programação em C#). As chaves compostas também podem ser usadas em um group cláusula.

Exemplo

O exemplo a seguir compara o resultado de uma associação interna, uma associação de grupo e uma junção externa esquerda mesmo fontes de dados usando as mesmas chaves correspondentes. Algum código extra é adicionado a esses exemplos para esclarecer os resultados na tela do console.

class JoinDemonstration
{
    #region Data

    class Product
    {
        public string Name { get; set; }
        public int CategoryID { get; set; }
    }

    class Category
    {
        public string Name { get; set; }
        public int ID { get; set; }
    }

    // Specify the first data source.
    List<Category> categories = new List<Category>()
    { 
        new Category(){Name="Beverages", ID=001},
        new Category(){ Name="Condiments", ID=002},
        new Category(){ Name="Vegetables", ID=003},
        new Category() {  Name="Grains", ID=004},
        new Category() {  Name="Fruit", ID=005}            
    };

    // Specify the second data source.
    List<Product> products = new List<Product>()
   {
      new Product{Name="Cola",  CategoryID=001},
      new Product{Name="Tea",  CategoryID=001},
      new Product{Name="Mustard", CategoryID=002},
      new Product{Name="Pickles", CategoryID=002},
      new Product{Name="Carrots", CategoryID=003},
      new Product{Name="Bok Choy", CategoryID=003},
      new Product{Name="Peaches", CategoryID=005},
      new Product{Name="Melons", CategoryID=005},
    };
    #endregion


    static void Main(string[] args)
    {
        JoinDemonstration app = new JoinDemonstration();

        app.InnerJoin();
        app.GroupJoin();
        app.GroupInnerJoin();
        app.GroupJoin3();
        app.LeftOuterJoin();
        app.LeftOuterJoin2();

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    void InnerJoin()
    {
        // Create the query that selects  
        // a property from each element. 
        var innerJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID
           select new { Category = category.ID, Product = prod.Name };

        Console.WriteLine("InnerJoin:");
        // Execute the query. Access results  
        // with a simple foreach statement. 
        foreach (var item in innerJoinQuery)
        {
            Console.WriteLine("{0,-10}{1}", item.Product, item.Category);
        }
        Console.WriteLine("InnerJoin: {0} items in 1 group.", innerJoinQuery.Count());
        Console.WriteLine(System.Environment.NewLine);

    }

    void GroupJoin()
    {
        // This is a demonstration query to show the output 
        // of a "raw" group join. A more typical group join 
        // is shown in the GroupInnerJoin method. 
        var groupJoinQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup;

        // Store the count of total items (for demonstration only). 
        int totalItems = 0;

        Console.WriteLine("Simple GroupJoin:");

        // A nested foreach statement is required to access group items. 
        foreach (var prodGrouping in groupJoinQuery)
        {
            Console.WriteLine("Group:");
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("   {0,-10}{1}", item.Name, item.CategoryID);
            }
        }
        Console.WriteLine("Unshaped GroupJoin: {0} items in {1} unnamed groups", totalItems, groupJoinQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void GroupInnerJoin()
    {
        var groupJoinQuery2 =
            from category in categories
            orderby category.ID
            join prod in products on category.ID equals prod.CategoryID into prodGroup
            select new
            {
                Category = category.Name,
                Products = from prod2 in prodGroup
                           orderby prod2.Name
                           select prod2
            };

        //Console.WriteLine("GroupInnerJoin:");
        int totalItems = 0;

        Console.WriteLine("GroupInnerJoin:");
        foreach (var productGroup in groupJoinQuery2)
        {
            Console.WriteLine(productGroup.Category);
            foreach (var prodItem in productGroup.Products)
            {
                totalItems++;
                Console.WriteLine("  {0,-10} {1}", prodItem.Name, prodItem.CategoryID);
            }
        }
        Console.WriteLine("GroupInnerJoin: {0} items in {1} named groups", totalItems, groupJoinQuery2.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void GroupJoin3()
    {

        var groupJoinQuery3 =
            from category in categories
            join product in products on category.ID equals product.CategoryID into prodGroup
            from prod in prodGroup
            orderby prod.CategoryID
            select new { Category = prod.CategoryID, ProductName = prod.Name };

        //Console.WriteLine("GroupInnerJoin:");
        int totalItems = 0;

        Console.WriteLine("GroupJoin3:");
        foreach (var item in groupJoinQuery3)
        {
            totalItems++;
            Console.WriteLine("   {0}:{1}", item.ProductName, item.Category);
        }

        Console.WriteLine("GroupJoin3: {0} items in 1 group", totalItems, groupJoinQuery3.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void LeftOuterJoin()
    {
        // Create the query. 
        var leftOuterQuery =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           select prodGroup.DefaultIfEmpty(new Product() { Name = "Nothing!", CategoryID = category.ID });

        // Store the count of total items (for demonstration only). 
        int totalItems = 0;

        Console.WriteLine("Left Outer Join:");

        // A nested foreach statement  is required to access group items 
        foreach (var prodGrouping in leftOuterQuery)
        {
            Console.WriteLine("Group:", prodGrouping.Count());
            foreach (var item in prodGrouping)
            {
                totalItems++;
                Console.WriteLine("  {0,-10}{1}", item.Name, item.CategoryID);
            }
        }
        Console.WriteLine("LeftOuterJoin: {0} items in {1} groups", totalItems, leftOuterQuery.Count());
        Console.WriteLine(System.Environment.NewLine);
    }

    void LeftOuterJoin2()
    {
        // Create the query. 
        var leftOuterQuery2 =
           from category in categories
           join prod in products on category.ID equals prod.CategoryID into prodGroup
           from item in prodGroup.DefaultIfEmpty()
           select new { Name = item == null ? "Nothing!" : item.Name, CategoryID = category.ID };

        Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", leftOuterQuery2.Count());
        // Store the count of total items 
        int totalItems = 0;

        Console.WriteLine("Left Outer Join 2:");

        // Groups have been flattened. 
        foreach (var item in leftOuterQuery2)
        {
            totalItems++;
            Console.WriteLine("{0,-10}{1}", item.Name, item.CategoryID);
        }
        Console.WriteLine("LeftOuterJoin2: {0} items in 1 group", totalItems);
    }
}
/*Output:

InnerJoin:
Cola      1
Tea       1
Mustard   2
Pickles   2
Carrots   3
Bok Choy  3
Peaches   5
Melons    5
InnerJoin: 8 items in 1 group.


Unshaped GroupJoin:
Group:
    Cola      1
    Tea       1
Group:
    Mustard   2
    Pickles   2
Group:
    Carrots   3
    Bok Choy  3
Group:
Group:
    Peaches   5
    Melons    5
Unshaped GroupJoin: 8 items in 5 unnamed groups


GroupInnerJoin:
Beverages
    Cola       1
    Tea        1
Condiments
    Mustard    2
    Pickles    2
Vegetables
    Bok Choy   3
    Carrots    3
Grains
Fruit
    Melons     5
    Peaches    5
GroupInnerJoin: 8 items in 5 named groups


GroupJoin3:
    Cola:1
    Tea:1
    Mustard:2
    Pickles:2
    Carrots:3
    Bok Choy:3
    Peaches:5
    Melons:5
GroupJoin3: 8 items in 1 group


Left Outer Join:
Group:
    Cola      1
    Tea       1
Group:
    Mustard   2
    Pickles   2
Group:
    Carrots   3
    Bok Choy  3
Group:
    Nothing!  4
Group:
    Peaches   5
    Melons    5
LeftOuterJoin: 9 items in 5 groups


LeftOuterJoin2: 9 items in 1 group
Left Outer Join 2:
Cola      1
Tea       1
Mustard   2
Pickles   2
Carrots   3
Bok Choy  3
Nothing!  4
Peaches   5
Melons    5
LeftOuterJoin2: 9 items in 1 group
Press any key to exit.
*/

Comentários

A join cláusula que não é seguida por into é traduzido em um Join``4 chamada de método. A join cláusula que é seguida por into é traduzido como um GroupJoin``4 chamada de método.

Consulte também

Tarefas

Como executar junções externas esquerdas (Guia de Programação em C#)

Como executar junções internas (Guia de Programação em C#)

Como executar junções agrupadas (Guia de Programação em C#)

Como ordenar os resultados de uma cláusula join (Guia de Programação em C#)

Como unir usando chaves compostas (Guia de Programação em C#)

Como instalar bancos de dados de exemplo

Referência

Cláusula group (Referência de C#)

Conceitos

Expressões de consulta LINQ (Guia de Programação em C#)

Operações join

Outros recursos

Palavras-chave de consulta (Referência de C#)