私はあなたのことを完全にはフォローしていないので、別の例を挙げます。それを使用して、参加の基本を説明し、うまくいけば、あなたが学ぶ必要があることを打ちます。
LocationThingなど(私が失った)よりも少し意味のある名前のクラスが2つあると想像してみましょう。
public class Language
{
string Code{get;set;}
string EnglishName{get;set;}
string NativeName{get;set;}
}
public class Document
{
public int ID{get; private set;}//no public set as it corresponds to an automatically-set column
public string LanguageCode{get;set;}
public string Title{get;set;}
public string Text{get;set;}
}
ここで、すべての言語とドキュメントをそれぞれ返すメソッドがあるGetLanguages()
と想像してみましょう。GetDocuments()
動作する方法はいくつかありますが、後で説明します。
結合が役立つ例としては、たとえば、すべてのタイトルと、それらが使用されている言語のすべての英語名が必要な場合があります。SQLでは、次のように使用します。
SELECT documents.title, languages.englishName
FROM languages JOIN documents
ON languages.code = documents.languageCode
または、テーブル名を省略しても列名があいまいにならない場合は、次のようにします。
SELECT title, englishName
FROM languages JOIN documents
ON code = languageCode
これらのそれぞれは、ドキュメントの各行について、言語の対応する行と一致し、結合された行のタイトルと英語名を返します(一致する言語のないドキュメントがある場合、それは返されません。同じコードを持つ2つの言語があります-この場合はdbによって防止する必要があります-対応するドキュメントはそれぞれに1回言及されます)。
LINQに相当するものは次のとおりです。
from l in GetLanguages()
join d in GetDocuments()
on l.Code equals d.LanguageCode //note l must come before d
select new{d.Title, l.EnglishName}
これは同様に各ドキュメントを対応する言語と照合し、IQueryable<T>
またはIEnumerable<T>
(ソース列挙/クエリ可能オブジェクトに応じて)を返します。ここで、はプロパティT
を持つ匿名オブジェクトです。Title
EnglishName
さて、これの費用について。これは主にとの性質に依存しGetLanguages()
ますGetDocuments()
。
ソースが何であれ、これは本質的に、これら2つのメソッドの結果をすべて検索することです。これは操作の性質にすぎません。ただし、これを行う最も効率的な方法は、ソースデータについて私たちが知っていることによって異なります。最初にLinq2Objectsフォームについて考えてみましょう。これを行う方法はたくさんありますが、List
事前に計算されたを返していると想像してみてください。
public List<Document> GetDocuments()
{
return _precomputedDocs;
}
public List<Language> GetLanguages()
{
return _precomputedLangs;
}
Linqjoin
がしばらく存在しないふりをして、上記のコードと機能的に同等の何かをどのように書くか想像してみましょう。次のようなものに到達する可能性があります。
var langLookup = GetLanguages().ToLookup(l => l.Code);
foreach(var doc in GetDocuments())
foreach(var lang in langLookup[doc.LanguageCode])
yield return new{doc.Title, lang.EnglishName};
これは合理的な一般的なケースです。各言語で最終的に気になるのは英語の名前だけであることがわかっているので、さらに一歩進んでストレージを減らすことができます。
var langLookup = GetLanguages().ToLookup(l => l.Code, l => l.EnglishName);
foreach(var doc in GetDocuments())
foreach(var englishName in langLookup[doc.LanguageCode])
yield return new{doc.Title, EnglishName = englishName};
これは、データセットに関する特別な知識がなくてもできることとほぼ同じです。
特別な知識があれば、さらに先に進むことができます。たとえば、コードごとに言語が1つしかないことがわかっている場合は、次の方が高速になります。
var langLookup = GetLanguages().ToDictionary(l => l.Code, l => l.EnglishName);
string englishName;
foreach(var doc in GetDocuments())
if(langLookup.TryGetValue(doc.LanguageCode, out englishName))
yield return new{doc.Title, EnglishName = englishName};
2つのソースが両方とも言語コードでソートされていることがわかっている場合は、さらに進んで両方を同時にスピンし、一致を生成し、処理した言語を破棄することができます。列挙の残りの部分で再度必要になります。
しかし、Linqは、2つのリストを見るだけでは、そのような特別な知識を持っていません。すべての人にとって、それはすべての単一の言語を知っており、すべての単一の文書はすべて同じコードを持っています。それは本当に見つけるためにたくさんを調べる必要があります。そのため、それはそれを行う方法でかなり効率的です(いくつかの最適化のために、上記の私の例が示唆するよりも少し良いです)。
Linq2SQLの場合を考えてみましょう。また、EntityFrameworkとデータベースでLinqを直接使用する他の方法も同等であることに注意してください。_ctx
これがすべて、であるメンバーを持つクラスのコンテキストで発生しているとしましょうDataContext
。次に、ソースメソッドは次のようになります。
public Table<Document> GetDocuments()
{
return _ctx.GetTable<Document>();
}
public Table<Language> GetLanguages()
{
return _ctx.GetTable<Languages>();
}
Table<T>
IQueryable<T>
他のいくつかのメソッドと一緒に実装します。ここでは、メモリ内で物事を結合する代わりに、次の(いくつかのエイリアスを除く)SQLを実行します。
SELECT documents.title, languages.englishName
FROM languages JOIN documents
ON languages.code = documents.languageCode
見覚えがあります?これは、最初に述べたのと同じSQLです。
これの最初の素晴らしい点は、使用しないデータベースから何も戻さないことです。
2つ目の優れた点は、データベースのクエリエンジン(これを実行可能コードに変換して実行するもの)がデータの性質を認識していることです。たとえばLanguages
、列に一意のキーまたは制約を設定するようにテーブルを設定した場合code
、エンジンは同じコードを持つ2つの言語が存在できないことを認識しているため、上記の最適化と同等の処理を実行できます。のDictionary
代わりに使用しましたILookup
。
3つ目の優れた点は、インデックスをオンにするlanguages.code
とdocuments.languageCode
、クエリエンジンがこれらを使用して、検索と照合をさらに高速化することです。おそらく、テーブルにヒットせずにインデックスから必要なものをすべて取得し、最初にヒットするテーブルを呼び出します。 2番目の関連性のない行のテストなどは避けてください。
第4に素晴らしいことは、RDBMSは、この種の検索を可能な限り高速化する方法に関する数十年の研究から恩恵を受けてきたということです。から利益を得る。
それでは、メモリ内のソースに対してではなく、データソースに対して直接クエリを実行する必要があります。例外があります。特に、いくつかの形式のグループ化(DBを直接いくつかのgroup-by操作でヒットすることは、それを繰り返しヒットすることを意味する可能性があります)と、同じ結果をすばやく連続して再利用する場合(この場合、ヒットする方がよい)です。それらの結果に対して1回、次にそれらを保存します)。