Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Satsen join
är användbar för att associera element från olika källsekvenser som inte har någon direkt relation i objektmodellen. Det enda kravet är att elementen i varje källa delar ett värde som kan jämföras för likhet. En livsmedelsdistributör kan till exempel ha en lista över leverantörer av en viss produkt och en lista över köpare. En join
-sats kan till exempel användas för att skapa en lista över leverantörer och köpare av den produkten som alla befinner sig i samma angivna region.
En join
-sats tar två källsekvenser som indata. Elementen i varje sekvens måste antingen vara eller innehålla en egenskap som kan jämföras med en motsvarande egenskap i den andra sekvensen. Satsen join
jämför de angivna nycklarna för likhet med hjälp av nyckelordet special equals
. Alla kopplingar som utförs av join
-satsen är likvärdiga. Formen på utdata från en join
-sats beror på vilken typ av koppling du utför. Följande är tre vanligaste kopplingstyper:
Inre sammanfogning
Gå med i grupp
Vänster yttre koppling
Inre sammanfogning
I följande exempel visas ett enkelt inre equijoin. Den här frågan skapar en platt sekvens med "produktnamn/kategori"-par. Samma kategoristräng visas i flera element. Om ett element från categories
inte har någon matchande products
visas inte den kategorin i resultatet.
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
Mer information finns i Utför inre kopplingar.
Gå med i grupp
En join
-sats med ett into
-uttryck kallas en gruppkoppling.
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 };
En gruppkoppling skapar en hierarkisk resultatsekvens som associerar element i den vänstra källsekvensen med ett eller flera matchande element i källsekvensen till höger. En gruppkoppling har ingen motsvarighet i relationella termer. det är i princip en sekvens av objektmatriser.
Om det inte finns några element från den högra källsekvensen som matchar ett element i den vänstra källan, skapar join
-satsen en tom matris för objektet. Därför är gruppkopplingen fortfarande i princip en inre equijoin, förutom att resultatsekvensen är organiserad i grupper.
Om du bara väljer resultatet av en gruppkoppling kan du komma åt objekten, men du kan inte identifiera den nyckel som de matchar på. Därför är det vanligtvis mer användbart att välja resultatet av gruppkopplingen till en ny typ som också har nyckelnamnet, som du ser i föregående exempel.
Du kan naturligtvis också använda resultatet av en gruppkoppling som generator för en annan underfråga:
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;
Mer information finns i Utför grupperade kopplingar.
Vänster yttre koppling
I en vänster yttre koppling returneras alla element i den vänstra källsekvensen, även om inga matchande element finns i den högra sekvensen. Om du vill utföra en vänster yttre koppling i LINQ använder du metoden DefaultIfEmpty
i kombination med en gruppkoppling för att ange ett standardelement på höger sida som ska skapas om ett element på vänster sida inte har några matchningar. Du kan använda null
som standardvärde för alla referenstyper eller ange en användardefinierad standardtyp. I följande exempel visas en användardefinierad standardtyp:
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 };
För mer information, se Utför vänster yttre kopplingar.
Operatorn lika med
En join
-sats utför ett likvärdigt jobb. Med andra ord kan du bara basera matchningar på likheten mellan två nycklar. Andra typer av jämförelser som "större än" eller "inte lika med" stöds inte. För att klargöra att alla kopplingar är likvärdiga använder join
-satsen nyckelordet equals
i stället för operatorn ==
. Nyckelordet equals
kan bara användas i en join
-sats och det skiljer sig från ==
-operatorn på några viktiga sätt. När du jämför strängar har equals
en överlagring för att jämföra efter värde och operatorn ==
använder referenslikhet. När båda sidor av jämförelsen har identiska strängvariabler, når equals
och ==
samma resultat: sant. Det beror på att när ett program deklarerar två eller flera motsvarande strängvariabler lagrar kompilatorn alla på samma plats. Detta kallas internering. En annan viktig skillnad är null-jämförelsen: null equals null
utvärderas som falskt med equals
-operatorn, istället för ==
-operatorn som utvärderar det som sant. Slutligen skiljer sig omfångsbeteendet: med equals
använder den vänstra nyckeln den yttre källsekvensen och den högra nyckeln förbrukar den inre källan. Den yttre källan är endast inom räckvidden på vänster sida av equals
, och den inre källsekvensen är endast inom räckvidden på höger sida.
Olikvärdeskopplingar
Du kan utföra icke-equijoins, korskopplingar och andra anpassade kopplingsåtgärder genom att använda flera from
-satser för att introducera nya sekvenser oberoende av varandra i en fråga. Mer information finns i Utföra anpassade kopplingsåtgärder.
Kopplingar till objektsamlingar jämfört med relationstabeller
I ett LINQ-frågeuttryck utförs kopplingsåtgärder på objektsamlingar. Objektsamlingar kan inte "kopplas" på exakt samma sätt som två relationstabeller. I LINQ krävs explicita join
-satser endast när två källsekvenser inte är knutna till någon relation. När du arbetar med LINQ till SQL representeras sekundärnyckeltabeller i objektmodellen som egenskaper för den primära tabellen. I Northwind-databasen har tabellen Kund till exempel en utländsk nyckelrelation med tabellen Beställningar. När du mappar tabellerna till objektmodellen har klassen Kund en orderegenskap som innehåller samlingen Beställningar som är associerade med kunden. I själva verket har kopplingen redan gjorts åt dig.
Mer information om hur du kör frågor mellan relaterade tabeller i samband med LINQ till SQL finns i How to: Map Database Relationships.
Sammansatta nycklar
Du kan testa likheten mellan flera värden med hjälp av en sammansatt nyckel. Mer information finns i Koppla med hjälp av sammansatta nycklar. Sammansatta nycklar kan också användas i en group
-sats.
Exempel
I följande exempel jämförs resultatet av en inre koppling, en gruppkoppling och en vänster yttre koppling i samma datakällor med hjälp av samma matchande nycklar. Lite extra kod läggs till i de här exemplen för att klargöra resultatet i konsolvisningen.
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.
*/
Anmärkningar
En join
-sats som inte följs av into
översätts till ett Join metodanrop. En join
-sats som följs av into
översätts till ett GroupJoin metodanrop.
Se även
- frågenyckelord (LINQ)
- Language Integrated Query (LINQ)
- Sammanfogningsoperationer
- gruppklausul
- Utför vänster yttre sammankopplingar
- Utför inre kopplingar
- Utför grupperade kopplingar
- Sortera resultaten av en sammanslagningsvillkor
- Koppla med hjälp av sammansatta nycklar
- Kompatibla databassystem för Visual Studio