join (Cláusula, Referencia de C#)join clause (C# Reference)

La cláusula join es útil para asociar elementos de secuencias de origen diferentes que no tienen ninguna relación directa en el modelo de objetos.The join clause is useful for associating elements from different source sequences that have no direct relationship in the object model. El único requisito es que los elementos de cada origen compartan algún valor del que se pueda comparar la igualdad.The only requirement is that the elements in each source share some value that can be compared for equality. Por ejemplo, imagínese que un distribuidor de comida tiene una lista de proveedores de un determinado producto y una lista de compradores.For example, a food distributor might have a list of suppliers of a certain product, and a list of buyers. Se puede usar una cláusula join, por ejemplo, para crear una lista de los proveedores y compradores de dicho producto que se encuentran en la misma región especificada.A join clause can be used, for example, to create a list of the suppliers and buyers of that product who are all in the same specified region.

La cláusula join toma dos secuencias de origen como entrada.A join clause takes two source sequences as input. Los elementos de cada secuencia deben ser o deben contener una propiedad que se pueda comparar con una propiedad correspondiente en la otra secuencia.The elements in each sequence must either be or contain a property that can be compared to a corresponding property in the other sequence. La cláusula join compara la igualdad de las claves especificadas mediante la palabra clave especial equals.The join clause compares the specified keys for equality by using the special equals keyword. Todas las combinaciones efectuadas por la cláusula join son combinaciones de igualdad.All joins performed by the join clause are equijoins. La forma de la salida de una cláusula join depende del tipo específico de combinación que se va a efectuar.The shape of the output of a join clause depends on the specific type of join you are performing. Estos son los tres tipos de combinación más comunes:The following are three most common join types:

  • Combinación internaInner join

  • Combinación agrupadaGroup join

  • Combinación externa izquierdaLeft outer join

Combinación internaInner join

En el ejemplo siguiente se muestra una combinación de igualdad interna simple.The following example shows a simple inner equijoin. Esta consulta genera una secuencia plana de pares de "nombre de producto y categoría".This query produces a flat sequence of "product name / category" pairs. La misma cadena de categoría aparecerá en varios elementos.The same category string will appear in multiple elements. Si un elemento de categories no tiene ningún products que coincida, dicha categoría no aparecerá en los resultados.If an element from categories has no matching products, that category will not appear in the results.

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 obtener más información, vea Realizar combinaciones internas.For more information, see Perform inner joins.

Combinación agrupadaGroup join

Una cláusula join con una expresión into se denomina "combinación agrupada".A join clause with an into expression is called a group join.

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 };

Las combinaciones agrupadas generan una secuencia de resultados jerárquicos que asocia los elementos de la secuencia de origen izquierda con uno o más elementos coincidentes de la secuencia de origen derecha.A group join produces a hierarchical result sequence, which associates elements in the left source sequence with one or more matching elements in the right side source sequence. Las combinaciones agrupadas no tienen ningún equivalente en términos relacionales; son básicamente una secuencia de matrices de objetos.A group join has no equivalent in relational terms; it is essentially a sequence of object arrays.

Si no se encuentra ningún elemento de la secuencia de origen derecha para que coincida con un elemento del origen izquierdo, la cláusula join generará una matriz vacía para ese elemento.If no elements from the right source sequence are found to match an element in the left source, the join clause will produce an empty array for that item. Por lo tanto, la combinación agrupada es básicamente una combinación de igualdad interna, salvo que la secuencia del resultado se organiza en grupos.Therefore, the group join is still basically an inner-equijoin except that the result sequence is organized into groups.

Si solo selecciona los resultados de una combinación agrupada, puede tener acceso a los elementos, pero no podrá identificar la clave en la que coinciden.If you just select the results of a group join, you can access the items, but you cannot identify the key that they match on. Por lo tanto, suele resultar más útil seleccionar los resultados de la combinación agrupada en un nuevo tipo que también tenga el nombre de la clave, como se muestra en el ejemplo anterior.Therefore, it is generally more useful to select the results of the group join into a new type that also has the key name, as shown in the previous example.

Por supuesto, también puede usar el resultado de una combinación agrupada como generador de otra subconsulta:You can also, of course, use the result of a group join as the generator of another subquery:

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 obtener más información, vea Realizar combinaciones agrupadas.For more information, see Perform grouped joins.

Combinación externa izquierdaLeft outer join

En una combinación externa izquierda se devuelven todos los elementos de la secuencia de origen izquierda, incluso si no hay elementos coincidentes en la secuencia derecha.In a left outer join, all the elements in the left source sequence are returned, even if no matching elements are in the right sequence. Para efectuar una combinación externa izquierda en LINQLINQ, use el método DefaultIfEmpty junto con una combinación agrupada para especificar un elemento derecho predeterminado para que se genere si un elemento izquierdo no tiene coincidencias.To perform a left outer join in LINQLINQ, use the DefaultIfEmpty method in combination with a group join to specify a default right-side element to produce if a left-side element has no matches. Puede usar null como valor predeterminado para cualquier tipo de referencia, aunque también puede especificar un tipo predeterminado definido por el usuario.You can use null as the default value for any reference type, or you can specify a user-defined default type. En el ejemplo siguiente se muestra un tipo predeterminado definido por el usuario:In the following example, a user-defined default type is shown:

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 obtener más información, vea Realizar operaciones de combinación externa izquierda.For more information, see Perform left outer joins.

El operador de igualdadThe equals operator

La cláusula join efectúa una combinación de igualdad.A join clause performs an equijoin. En otras palabras, solo puede basar las coincidencias en la igualdad de dos claves.In other words, you can only base matches on the equality of two keys. No se admiten otros tipos de comparaciones, como "mayor que" o "no es igual a".Other types of comparisons such as "greater than" or "not equals" are not supported. Para aclarar que todas las combinaciones son combinaciones de igualdad, la cláusula join usa la palabra clave equals en lugar del operador ==.To make clear that all joins are equijoins, the join clause uses the equals keyword instead of the == operator. La palabra clave equals solo se puede usar en una cláusula join y difiere del operador == en un aspecto importante.The equals keyword can only be used in a join clause and it differs from the == operator in one important way. Con equals, la clave izquierda usa la secuencia de origen externa, mientras que la clave derecha usa el origen interno.With equals, the left key consumes the outer source sequence, and the right key consumes the inner source. El origen externo solo está en el ámbito del lado izquierdo de equals, mientras que la secuencia de origen interna solo está en el ámbito del lado derecho.The outer source is only in scope on the left side of equals and the inner source sequence is only in scope on the right side.

Combinaciones de desigualdadNon-equijoins

Puede efectuar combinaciones de desigualdad, combinaciones cruzadas y otras operaciones de combinación personalizadas usando varias cláusulas from para introducir nuevas secuencias en una consulta de manera independiente.You can perform non-equijoins, cross joins, and other custom join operations by using multiple from clauses to introduce new sequences independently into a query. Para obtener más información, vea Realizar operaciones de combinación personalizadas.For more information, see Perform custom join operations.

Uniones en las colecciones de objetos frente a las tablas relacionalesJoins on object collections vs. relational tables

En una expresión de consulta LINQLINQ, las operaciones de combinación se efectúan en las colecciones de objetos.In a LINQLINQ query expression, join operations are performed on object collections. Las colecciones de objetos no se pueden "combinar" exactamente igual que dos tablas relacionales.Object collections cannot be "joined" in exactly the same way as two relational tables. En LINQLINQ, las cláusulas join explícitas solo son necesarias cuando dos secuencias de origen no están unidas por ninguna relación.In LINQLINQ, explicit join clauses are only required when two source sequences are not tied by any relationship. Cuando se trabaja con LINQ to SQLLINQ to SQL, las tablas de claves externas se representan en el modelo de objetos como propiedades de la tabla principal.When working with LINQ to SQLLINQ to SQL, foreign key tables are represented in the object model as properties of the primary table. Por ejemplo, en la base de datos Northwind, la tabla Customer (Cliente) tiene una relación de clave externa con la tabla Orders (Pedidos).For example, in the Northwind database, the Customer table has a foreign key relationship with the Orders table. Al asignar las tablas al modelo de objetos, la clase Customer tiene una propiedad Orders que contiene la colección Orders asociada a ese cliente.When you map the tables to the object model, the Customer class has an Orders property that contains the collection of Orders associated with that Customer. De hecho, la combinación ya se ha llevado a cabo automáticamente.In effect, the join has already been done for you.

Para obtener más información sobre las consultas en tablas relacionadas en el contexto de LINQ to SQLLINQ to SQL, vea Cómo: Asignar relaciones de base de datos.For more information about querying across related tables in the context of LINQ to SQLLINQ to SQL, see How to: Map Database Relationships.

Claves compuestasComposite keys

Puede probar la igualdad de varios valores mediante una clave compuesta.You can test for equality of multiple values by using a composite key. Para obtener más información, vea Realizar una unión usando claves compuestas.For more information, see Join by using composite keys. Las claves compuestas también se pueden usar en una cláusula group.Composite keys can be also used in a group clause.

EjemploExample

En el ejemplo siguiente se comparan los resultados de una combinación interna, una combinación agrupada y una combinación externa izquierda en los mismos orígenes de datos mediante las mismas claves coincidentes.The following example compares the results of an inner join, a group join, and a left outer join on the same data sources by using the same matching keys. Se ha agregado algún código adicional a estos ejemplos para aclarar los resultados en la pantalla de la consola.Some extra code is added to these examples to clarify the results in the console display.

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

ComentariosRemarks

Una cláusula join que no va seguida de into se convierte en una llamada al método Join.A join clause that is not followed by into is translated into a Join method call. Una cláusula join que va seguida de into se convierte en una llamada al método GroupJoin.A join clause that is followed by into is translated to a GroupJoin method call.

Vea tambiénSee also