Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Klauzula jest przydatna join
do kojarzenia elementów z różnych sekwencji źródłowych, które nie mają bezpośredniej relacji w modelu obiektów. Jedynym wymaganiem jest, aby elementy w każdym źródle posiadały wspólną wartość, którą można porównać pod względem równości. Na przykład dystrybutor żywności może mieć listę dostawców określonego produktu i listę nabywców. Można join
na przykład użyć klauzuli , aby utworzyć listę dostawców i nabywców tego produktu, którzy znajdują się w tym samym regionie.
Klauzula join
przyjmuje dwie sekwencje źródłowe jako dane wejściowe. Elementy w każdej sekwencji muszą być albo zawierać właściwość, którą można porównać z odpowiednią właściwością w innej sekwencji. Klauzula join
porównuje określone klucze pod kątem równości przy użyciu specjalnego equals
słowa kluczowego. Wszystkie sprzężenia wykonywane przez klauzulę join
są sprzężeniami równościowymi. Kształt danych wyjściowych klauzuli join
zależy od określonego typu sprzężenia, które wykonujesz. Poniżej przedstawiono trzy najbardziej typowe typy sprzężenia:
Sprzężenie wewnętrzne
Dołącz do grupy
Lewe zewnętrzne połączenie
Sprzężenie wewnętrzne
W poniższym przykładzie pokazano proste wewnętrzne łączenie równościowe. To zapytanie tworzy płaską sekwencję par "nazwa produktu/kategoria". Ten sam ciąg kategorii będzie wyświetlany w wielu elementach. Jeśli element z categories
nie ma pasującego elementu z products
, ta kategoria nie pojawi się w wynikach.
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
Aby uzyskać więcej informacji, zobacz Wykonywanie sprzężeń wewnętrznych.
Dołącz do grupy
Klauzula join
z wyrażeniem into
jest nazywana sprzężeniami grupowymi.
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 };
Połączenie grupowe tworzy hierarchiczną sekwencję wyników, która łączy elementy w sekwencji źródłowej z lewej strony z jednym lub więcej pasującymi elementami w sekwencji źródłowej po prawej stronie. Łączenie grup nie ma odpowiednika w kategoriach relacyjnych; zasadniczo jest to sekwencja tablic obiektów.
Jeśli nie znaleziono żadnych elementów z prawej sekwencji źródłowej pasujących do elementu w lewym źródle, klauzula join
spowoduje wygenerowanie pustej tablicy dla tego elementu. W związku z tym łączenie grupowe jest zasadniczo wciąż łączeniem wewnętrznym, z tą różnicą, że sekwencja wyników jest zorganizowana w grupy.
Jeśli po prostu wybierzesz wyniki łącznia grupowego, możesz uzyskać dostęp do elementów, ale nie możesz zidentyfikować klucza, z którym są powiązane. W związku z tym zazwyczaj bardziej przydatne jest wybranie wyników sprzężenia grupy w nowy typ, który ma również nazwę klucza, jak pokazano w poprzednim przykładzie.
Możesz również oczywiście użyć wyniku sprzężenia grupy jako generatora innego podzapytania:
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;
Aby uzyskać więcej informacji, zobacz Wykonywanie sprzężeń grupowanych.
Lewe zewnętrzne połączenie
W lewym zewnętrznym sprzężeniu zwracane są wszystkie elementy z lewego źródła, nawet jeśli w prawej sekwencji nie ma pasujących elementów. Aby wykonać zewnętrzne sprzężenie lewe w LINQ, użyj metody DefaultIfEmpty
w połączeniu z grupowaniem, aby określić domyślny element po prawej stronie do utworzenia, jeśli element po lewej stronie nie ma dopasowań. Można użyć null
jako wartości domyślnej dla dowolnego typu odwołania lub określić typ domyślny zdefiniowany przez użytkownika. W poniższym przykładzie pokazano domyślny typ zdefiniowany przez użytkownika:
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 };
Aby uzyskać więcej informacji, zobacz Wykonywanie sprzężeń zewnętrznych lewostronnych.
Operator równości
Klauzula join
wykonuje połączenie równościowe. Innymi słowy, można dopasowania opierać tylko na równości dwóch kluczy. Inne typy porównań, takie jak "większe niż" lub "nie równe", nie są obsługiwane. Aby wyjaśnić, że wszystkie łączenia to łączenia równoważnościowe, klauzula join
używa słowa kluczowego equals
zamiast operatora ==
. Słowo equals
kluczowe może być używane tylko w klauzuli join
i różni się od ==
operatora na kilka ważnych sposobów. Podczas porównywania ciągów equals
ma przeciążenie do porównania według wartości, a operator ==
sprawdza równość odwołań. Gdy obie strony porównania mają identyczne zmienne ciągu, equals
i ==
osiągną ten sam wynik: true. Dzieje się tak dlatego, że gdy program deklaruje co najmniej dwie równoważne zmienne ciągu, kompilator przechowuje wszystkie z nich w tej samej lokalizacji. Jest to nazywane internowaniem. Kolejną ważną różnicą jest porównanie wartości null: null equals null
jest oceniana jako fałsz za pomocą operatora equals
, zamiast operatora ==
, który ocenia ją jako prawda. Ponadto, zachowanie określania zakresu jest inne: w przypadku equals
, lewy przycisk korzysta z zewnętrznej sekwencji źródłowej, a prawy przycisk korzysta z wewnętrznego źródła. Źródło zewnętrzne znajduje się tylko w zakresie działania po lewej stronie equals
, a sekwencja źródła wewnętrznego znajduje się tylko w zakresie działania po prawej stronie.
Niezrównane
Można wykonywać sprzężenia nierównościowe, sprzężenia krzyżowe i inne niestandardowe operacje sprzężenia, używając wielu klauzuli from
, aby niezależnie wprowadzać nowe sekwencje do zapytania. Aby uzyskać więcej informacji, zobacz Wykonywanie niestandardowych operacji sprzężenia.
Łączenia w kolekcjach obiektów a tabele relacyjne
W wyrażeniu zapytania LINQ operacje sprzężenia są wykonywane w kolekcjach obiektów. Kolekcje obiektów nie mogą być "sprzężone" w dokładnie taki sam sposób jak dwie tabele relacyjne. W linQ klauzule jawne join
są wymagane tylko wtedy, gdy dwie sekwencje źródłowe nie są powiązane przez żadną relację. Podczas pracy z LINQ to SQL tabele kluczy obcych są reprezentowane w modelu obiektów jako właściwości tabeli podstawowej. Na przykład w bazie danych Northwind tabela Customer (Klient) ma relację klucza obcego z tabelą Orders (Zamówienia). Podczas mapowania tabel na model obiektów klasa Customer ma właściwość Orders zawierającą kolekcję Zamówień skojarzonych z tym klientem. W efekcie łączenie zostało już wykonane za Ciebie.
Aby uzyskać więcej informacji na temat wykonywania zapytań dotyczących powiązanych tabel w kontekście LINQ to SQL, zobacz How to: Map Database Relationships (Instrukcje: mapowanie relacji bazy danych).
Klucze złożone
Aby sprawdzić równość wielu wartości, możesz użyć klucza złożonego. Aby uzyskać więcej informacji, zobacz Join by using composite keys (Łączenie przy użyciu kluczy złożonych). Klucze złożone mogą być również używane w klauzuli group
.
Przykład
Poniższy przykład porównuje wyniki sprzężenia wewnętrznego, sprzężenia grupy i lewego sprzężenia zewnętrznego w tych samych źródłach danych przy użyciu tych samych pasujących kluczy. Do tych przykładów dodawany jest dodatkowy kod, aby wyjaśnić wyniki na ekranie konsoli.
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.
*/
Uwagi
Klauzula join
, która nie jest następowana przez into
, jest przekształcana w Join wywołanie metody. Klauzula, po której następuje join
, jest tłumaczona na wywołanie metody into
.
Zobacz też
- Słowa kluczowe zapytania (LINQ)
- Zintegrowane Zapytania Językowe (LINQ)
- Operacje sprzężenia
- klauzula grupowa
- Wykonywanie sprzężeń zewnętrznych po lewej stronie
- Wykonywanie sprzężeń wewnętrznych
- Wykonywanie sprzężeń grupowanych
- Posortuj wyniki klauzuli join
- Łączenie przy użyciu kluczy złożonych
- Zgodne systemy baz danych dla programu Visual Studio