Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
La join cláusula es útil para asociar elementos de diferentes secuencias de origen que no tienen ninguna relación directa en el modelo de objetos. El único requisito es que los elementos de cada origen compartan algún valor que se pueda comparar para obtener igualdad. Por ejemplo, un distribuidor de alimentos podría tener una lista de proveedores de un determinado producto y una lista de compradores. Se join puede usar una cláusula , por ejemplo, para crear una lista de los proveedores y compradores de ese producto que están todos en la misma región especificada.
Una join cláusula toma dos secuencias de origen como entrada. Los elementos de cada secuencia deben ser o contener una propiedad que se pueda comparar con una propiedad correspondiente en la otra secuencia. La join cláusula compara las claves especificadas para la igualdad mediante la palabra clave especial equals . Todas las combinaciones realizadas por la join cláusula son equijonas. La forma de la salida de una join cláusula depende del tipo específico de combinación que está realizando. A continuación se muestran tres tipos de combinación más comunes:
Combinación interna
Unirse a un grupo
Combinación externa izquierda
Combinación interna
En el ejemplo siguiente se muestra una equijoína interna simple. Esta consulta genera una secuencia plana de pares "nombre de producto/categoría". La misma cadena de categoría aparecerá en varios elementos. Si un elemento de categories no tiene ninguna coincidencia products, esa categoría no aparecerá en los 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 obtener más información, consulte Realizar combinaciones internas.
Unirse a un grupo
Una join cláusula con una into expresión se denomina combinación 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 };
Una combinación de grupo genera una secuencia de resultados jerárquica, que asocia elementos de la secuencia de origen izquierda a uno o varios elementos coincidentes en la secuencia de origen del lado derecho. Una combinación de grupo no tiene equivalente en términos relacionales; es básicamente una secuencia de matrices de objetos.
Si no se encuentra ningún elemento de la secuencia de origen derecha para que coincida con un elemento del origen izquierdo, la join cláusula generará una matriz vacía para ese elemento. Por lo tanto, la combinación de grupos sigue siendo básicamente una equijoína interna, salvo que la secuencia de resultados se organiza en grupos.
Si solo selecciona los resultados de una combinación de grupo, puede acceder a los elementos, pero no puede identificar la clave en la que coinciden. Por lo tanto, suele ser más útil seleccionar los resultados de la combinación de grupo en un nuevo tipo que también tiene el nombre de clave, como se muestra en el ejemplo anterior.
También puede, por supuesto, usar el resultado de una combinación de grupo como generador de otra 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 obtener más información, consulte Realizar combinaciones agrupadas.
Combinación externa izquierda
En una combinación externa izquierda, se devuelven todos los elementos de la secuencia de origen izquierda, aunque no haya elementos coincidentes en la secuencia derecha. Para realizar una combinación externa izquierda en LINQ, use el DefaultIfEmpty método en combinación con una combinación de grupo para especificar un elemento predeterminado del lado derecho para generar si un elemento del lado izquierdo no tiene coincidencias. Puede usar null como valor predeterminado para cualquier tipo de referencia o puede especificar un tipo predeterminado definido por el usuario. En el ejemplo siguiente, se muestra un tipo predeterminado definido por el usuario:
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 combinaciones externas izquierdas.
Operador equals
Una join cláusula realiza una equijoína. En otras palabras, solo puede basar coincidencias en la igualdad de dos claves. No se admiten otros tipos de comparaciones como "mayor que" o "no iguales". Para aclarar que todas las combinaciones son equijoins, la join cláusula usa la equals palabra clave en lugar del == operador . La equals palabra clave solo se puede usar en una join cláusula y difiere del == operador de algunas maneras importantes. Al comparar cadenas, equals tiene una sobrecarga para comparar por valor y el operador == usa la igualdad de referencia. Cuando ambos lados de la comparación tienen variables equals de cadena idénticas y == alcanzan el mismo resultado: true. Esto se debe a que, cuando un programa declara dos o más variables de cadena equivalentes, el compilador almacena todos ellos en la misma ubicación. Esto se conoce como internamiento. Otra diferencia importante es la comparación nula: null equals null se evalúa como false con equals operador, en lugar de == operador que lo evalúa como true. Por último, el comportamiento de ámbito es diferente: con equals, la clave izquierda consume la secuencia de origen externa y la clave derecha consume el origen interno. El origen externo solo está en el ámbito del lado izquierdo de equals y la secuencia de origen interna solo está en el ámbito del lado derecho.
No equijonos
Puede realizar combinaciones cruzadas, combinaciones cruzadas y otras operaciones de combinación personalizadas mediante varias from cláusulas para introducir nuevas secuencias de forma independiente en una consulta. Para obtener más información, consulte Realizar operaciones de combinación personalizadas.
Combinaciones en colecciones de objetos frente a tablas relacionales
En una expresión de consulta LINQ, las operaciones de combinación se realizan en colecciones de objetos. Las colecciones de objetos no se pueden "combinar" exactamente de la misma manera que dos tablas relacionales. En LINQ, las cláusulas explícitas join solo son necesarias cuando dos secuencias de origen no están vinculadas por ninguna relación. Al trabajar con LINQ to SQL, las tablas de clave externa se representan en el modelo de objetos como propiedades de la tabla principal. Por ejemplo, en la base de datos Northwind, la tabla Customer tiene una relación de clave externa con la tabla Orders. 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. En efecto, la combinación ya se ha realizado para usted.
Para obtener más información sobre cómo consultar tablas relacionadas en el contexto de LINQ to SQL, vea How to: Map Database Relationships.
Claves compuestas
Puede probar la igualdad de varios valores mediante una clave compuesta. Para obtener más información, consulte Combinación mediante claves compuestas. Las claves compuestas también se pueden usar en una group cláusula .
Ejemplo
En el ejemplo siguiente se comparan los resultados de una combinación interna, una combinación de grupo y una combinación externa izquierda en los mismos orígenes de datos mediante las mismas claves coincidentes. Se agrega código adicional a estos ejemplos para aclarar los resultados en la pantalla de la consola.
class JoinDemonstration
{
#region Data
class Product
{
public required string Name { get; init; }
public required int CategoryID { get; init; }
}
class Category
{
public required string Name { get; init; }
public required int ID { get; init; }
}
// Specify the first data source.
List<Category> categories =
[
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 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();
}
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: {innerJoinQuery.Count()} items in 1 group.");
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: {totalItems} items in {groupJoinQuery.Count()} unnamed groups");
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: {totalItems} items in {groupJoinQuery2.Count()} named groups");
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($" {item.ProductName}:{item.Category}");
}
Console.WriteLine($"GroupJoin3: {totalItems} items in 1 group");
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:");
foreach (var item in prodGrouping)
{
totalItems++;
Console.WriteLine(" {0,-10}{1}", item.Name, item.CategoryID);
}
}
Console.WriteLine($"LeftOuterJoin: {totalItems} items in {leftOuterQuery.Count()} groups");
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: {leftOuterQuery2.Count()} items in 1 group");
// 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: {totalItems} items in 1 group");
}
}
/*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.
*/
Observaciones
Una join cláusula que no va into seguida de se traduce en una Join llamada de método. Una join cláusula seguida de into se traduce a una GroupJoin llamada de método.
Consulte también
- Palabras clave de consulta (LINQ)
- Language Integrated Query (LINQ)
- Operaciones de unión
- group (cláusula)
- Realizar combinaciones externas izquierdas
- Realizar combinaciones internas
- Realizar combinaciones agrupadas
- Ordenar los resultados de una cláusula join
- Combinación mediante claves compuestas
- Sistemas de bases de datos compatibles para Visual Studio