Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Klauzule #D0 je užitečná pro přidružení prvků z různých zdrojových sekvencí, které nemají v objektovém modelu žádný přímý vztah. Jediným požadavkem je, aby prvky v každém zdroji sdílely určitou hodnotu, kterou je možné porovnat s rovností. Například distributor potravin může mít seznam dodavatelů určitého výrobku a seznam kupujících. Klauzuli #D0 lze použít například k vytvoření seznamu dodavatelů a kupujících daného produktu, kteří jsou všichni ve stejné zadané oblasti.
Klauzule join
přijímá jako vstup dvě zdrojové sekvence. Prvky v každé sekvenci musí být nebo musí obsahovat vlastnost, která se dá porovnat s odpovídající vlastností v druhé sekvenci. Klauzule #D0 porovnává zadané klíče rovnosti pomocí speciálního klíčového slova #D1. Všechna spojení prováděná klauzulí join
jsou rovnostní spojení. Tvar výstupu klauzule join
závisí na daném typu spojení, které provádíte. Toto jsou tři nejběžnější typy spojení:
Vnitřní spojení
Připojení ke skupině
Levé vnější spojení
Vnitřní spojení
Následující příklad ukazuje jednoduchý vnitřní ekvijoin. Tento dotaz vytvoří plochou sekvenci párů "název produktu / kategorie". Stejný řetězec kategorie se zobrazí ve více prvcích. Pokud prvek z #D0 nemá žádné odpovídající #B1 , tato kategorie se ve výsledcích nezobrazí.
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
Další informace viz Provádění vnitřních spojení.
Připojení ke skupině
Klauzule join
s výrazem into
se nazývá skupinové spojení.
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 };
Spojení skupiny vytvoří hierarchickou sekvenci výsledků, která přidruží prvky v levé zdrojové sekvenci k jednomu nebo více odpovídajícím prvkům v pravé sekvenci zdroje. Spojení skupiny nemá žádný ekvivalent v relačních termínech; je to v podstatě posloupnost polí objektů.
Pokud nejsou nalezeny žádné prvky z správné zdrojové sekvence, které by odpovídaly prvku v levém zdroji, klauzule #D0 vytvoří pro tuto položku prázdné pole. Tudíž je spojení skupin v podstatě vnitřní ekvijoin s tím rozdílem, že výsledek je uspořádán do skupin.
Pokud vyberete jenom výsledky připojení ke skupině, budete mít přístup k položkám, ale nemůžete identifikovat klíč, na který se shodují. Proto je obecně užitečnější vybrat výsledky operace spojení skupiny do nového typu, který má také název klíče, jak bylo ukázáno v předchozím příkladu.
Můžete také samozřejmě použít výsledek spojení skupiny jako generátor jiného poddotazu:
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;
Další informace naleznete v tématu Provádění seskupených spojení.
Levé vnější spojení (LEFT OUTER JOIN)
V levém vnějším spojení se vrátí všechny prvky v levé zdrojové sekvenci, i když nejsou v správné sekvenci žádné odpovídající prvky. Chcete-li provést levé vnější spojení v LINQ, použijte metodu DefaultIfEmpty
v kombinaci se skupinovým spojením pro určení výchozího prvku na pravé straně, který se vytvoří, pokud prvek na levé straně nemá žádné shody. Jako výchozí hodnotu pro libovolný typ odkazu můžete použít null
, nebo můžete zadat uživatelsky definovaný výchozí typ. V následujícím příkladu je zobrazen výchozí typ definovaný uživatelem:
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 };
Další informace naleznete v Provedení levých vnějších spojení.
Operátor rovná se
Klauzule join
provádí přirozené spojení. Jinými slovy, můžete porovnávat pouze na základě rovnosti dvou klíčů. Jiné typy porovnání, například "větší než" nebo "nerovná se", nejsou podporovány. Aby bylo zřejmé, že všechna spojení jsou equijoins, používá klauzule #D0 místo operátoru #D2 klíčové slovo #D1. Klíčové slovo #D0 lze použít pouze v klauzuli #D1 a liší se od operátoru #D2 několika důležitými způsoby. Při porovnávání řetězců má equals
přetížení pro porovnání podle hodnoty a operátor ==
používá kontrolu identity odkazů. Pokud obě strany porovnání mají identické řetězcové proměnné, equals
a ==
dosáhnou stejného výsledku: true. Je to proto, že když program deklaruje dvě nebo více ekvivalentních řetězcových proměnných, kompilátor je uloží do stejného umístění. To se označuje jako internování. Dalším důležitým rozdílem je porovnání s hodnotou null: #D0 se vyhodnotí jako nepravdivý s operátorem #D1 namísto operátoru #D2, který ho vyhodnotí jako pravdivý. Nakonec se chování rozsahu liší: s equals
, levé tlačítko využívá vnější zdrojovou sekvenci a pravé tlačítko využívá vnitřní zdrojovou sekvenci. Vnější zdroj je použitelný pouze na levé straně equals
a vnitřní zdrojová sekvence je použitelná pouze na pravé straně.
Nerovná spojení
K nezávislému zavedení nových sekvencí do dotazu můžete provádět neekvižné operace, křížové spojení a další vlastní operace spojení pomocí několika klauzulí #D0. Další informace naleznete v části Provádění vlastních operací spojení.
Spojení v rámci kolekcí objektů vs. relačních tabulek
Ve výrazu dotazu LINQ se operace spojení provádějí u kolekcí objektů. Kolekce objektů nelze "spojit" úplně stejným způsobem jako dvě relační tabulky. V LINQ jsou explicitní join
klauzule vyžadovány pouze v případě, že dvě zdrojové sekvence nejsou spojeny žádnou vazbou. Při práci s LINQ to SQL jsou tabulky cizích klíčů reprezentovány v objektovém modelu jako vlastnosti primární tabulky. Například v databázi Northwind má tabulka Customer relaci cizího klíče s tabulkou Orders. Při mapování tabulek na objektový model má třída Customer vlastnost Orders, která obsahuje kolekci Objednávek přidružených k danému zákazníkovi. Spojení bylo pro vás již provedeno.
Další informace o dotazování napříč souvisejícími tabulkami v kontextu LINQ to SQL naleznete v tématu Postupy: Mapování relací databáze.
Složené klávesy
Rovnost více hodnot můžete otestovat pomocí složeného klíče. Další informace naleznete v tématu Join pomocí složených klíčů. Složené klíče lze také použít v klauzuli group
.
Příklad
Následující příklad porovnává výsledky vnitřního spojení, spojení skupiny a levého vnějšího spojení ve stejných zdrojích dat pomocí stejných odpovídajících klíčů. Do těchto příkladů se přidá další kód, který objasní výsledky v zobrazení konzoly.
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.
*/
Poznámky
Klauzule #D0, která není následovaná #D1, se přeloží do volání metody #D2. Klauzule #D0, za kterou následuje #D1, se přeloží do volání metody #D2.
Viz také
- klíčová slova dotazu (LINQ)
- Jazykově integrovaný dotaz (LINQ)
- Operace spojení
- skupinová klauzule
- Provádějte levá vnější spojení
- Proveďte vnitřní spojení
- Provádět seskupené spojení
- Seřaďte výsledky klauzule spojení
- Připojit pomocí složených klíčů
- #B0 Kompatibilní databázové systémy pro Visual Studio #C1