0

私はデータベースと T-sql クエリが苦手なので、Linq を使用して C# で同様のことを行う方法に少し戸惑いました。

問題は、リレーショナル データベースのテーブルとほぼ同じ構造を持っていることです。これを使用して、何らかの結合選択を行う必要があります。

実際には、複合キー アドレスのリストを取得します。これらは実際にはいくつかの int 値を保持するクラスです (おそらく byte または short ですが、関連はありません)。ここで、これらのリストの一致を構造体で検索し、そこでメソッドを呼び出す必要があります。

これはおそらく単純な結合です (結合が何をするのか覚えていません) しかし、簡単に回避できるほど安くしたくないので、すべての行を検索する必要がないため、助けが必要です。すべてのアドレス。

public class TheLocationThing
{
    int ColumnID;
    int ColumnGroupId;
    int RowID;
}

public class TheCellThing
{
    TheLocationThing thing;

    public void MethodINeedToCallIfInList()
    {
        //here something happens
    }
}

public class TheRowThing
{
    int RowId;

    List<TheCellThing> CellsInThisRow;
}

public class TableThing
{
    List<TheRowThing> RowsInThisTable;
}

したがって、行とセルを持つこの表型クラスがあります。ColumnGroup に注意してください。これは ColumnId を含む複合キーであるため、同じ columnid を再度取得できますが、ColumnGroup ごとに 1 回だけです。

ただし、覚えておくべきことは、inn TheTable には GroupColumnId が 1 つしかないことですが、指定されたリストには複数の列がある可能性があるため、フィルターで除外できます。

public void DoThisThing()
{
    List<TheLocationThing> TheAddressesINeedToFind = GetTheseAddresses(); //actualy is a TheLocationThing[] if that matters

    var filterList = TheAddressesINeedToFind.Where(a => a.ColumnGroupId == this.CurrentActiveGroup);

    //Here I have to do the join with this.TableInstance
}

ここで、もちろん、その行で同じ行 ID を持つアドレスだけをループする必要があります。

また、ここで、特に最初のフィルターアウトで私を助ける何かをIQueryableとして管理していますか?それをQueryableとして取得する必要がありますか?

4

1 に答える 1

2

私はあなたのことを完全にはフォローしていないので、別の例を挙げます。それを使用して、参加の基本を説明し、うまくいけば、あなたが学ぶ必要があることを打ちます。

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を持つ匿名オブジェクトです。TitleEnglishName

さて、これの費用について。これは主にとの性質に依存し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.codedocuments.languageCode、クエリエンジンがこれらを使用して、検索と照合をさらに高速化することです。おそらく、テーブルにヒットせずにインデックスから必要なものをすべて取得し、最初にヒットするテーブルを呼び出します。 2番目の関連性のない行のテストなどは避けてください。

第4に素晴らしいことは、RDBMSは、この種の検索を可能な限り高速化する方法に関する数十年の研究から恩恵を受けてきたということです。から利益を得る。

それでは、メモリ内のソースに対してではなく、データソースに対して直接クエリを実行する必要があります。例外があります。特に、いくつかの形式のグループ化(DBを直接いくつかのgroup-by操作でヒットすることは、それを繰り返しヒットすることを意味する可能性があります)と、同じ結果をすばやく連続して再利用する場合(この場合、ヒットする方がよい)です。それらの結果に対して1回、次にそれらを保存します)。

于 2012-08-03T15:33:43.910 に答える