5

私はこのクエリを持っています:

var newComponents = from ic in importedComponents
                    where !existingComponents.Contains(ic)
                    select ic;

importedComponentsおよびexistingComponentsタイプList<ImportedComponent>は であり、メモリ内にのみ存在します (データ コンテキストに関連付けられていません)。この例でimportedComponentsは、6,100 をわずかに超えるアイテムがあり、existingComponents511 のアイテムがあります。

このステートメントは完了するのに時間がかかりすぎています (どのくらいかかるかはわかりませんが、20 分後にスクリプトを停止します)。実行速度の改善なしで次のことを試しました:

var existingComponentIDs = from ec in existingComponents
                           select ec.ID;

var newComponents = from ic in importedComponents
                    where !existingComponentIDs.Contains(ic.ID)
                    select ic;

どんな助けでも大歓迎です。

4

3 に答える 3

5

問題は、このアルゴリズムの二次複雑さです。すべての既存のコンポーネント ID の ID を HashSet に入れ、HashSet.Contains メソッドを使用します。リストの [Contains/Any] の O(N) と比較して、O(1) のルックアップ コストがあります。

morelinqプロジェクトには、 ExceptByという 1 つの便利な手順ですべてを行うメソッドが含まれています。

于 2012-04-20T23:07:46.323 に答える
4

Exceptセットの差を取得するために使用できます。

var existingComponentIDs = existingComponents.Select(c => c.ID); 
var importedComponentIDs = importedComponents.Select(c => c.ID);
var newComponentIDs = importedComponentIDs.Except(existingComponentIDs);
var newComponents = from ic in importedComponents
        join newID in newComponentIDs on ic.ID equals newID
        select ic;
foreach (var c in newComponents)
{ 
    // insert into database?
}

LINQ JOIN が WHERE でリンクするよりもはるかに高速なのはなぜですか?

つまり、 Join メソッドは、2 つのテーブルをすばやく圧縮するためのインデックスとして使用するハッシュ テーブルを設定できます。

于 2012-04-20T23:11:51.293 に答える
0

あなたが提供したロジックと数値に基づいて、そのステートメントを実行すると、基本的に 3117100 の比較を実行していることになります。配列全体を実行する前に条件が満たされる可能性があるため、明らかにそれは完全に正確ではありませんが、私の主張は理解できます。

コレクションがこれだけ大きい場合は、キー (この場合はコンポーネント ID) にインデックスを付けて検索のオーバーヘッドを削減できるコレクションを使用する必要があります。覚えておくべきことは、LINQ は SQL のように見えますが、ここには魔法のインデックスがないということです。これは主に利便性のためです。実際、リンク ルックアップがブルート フォース ルックアップよりも実際には少し遅いという記事を見たことがあります。

編集:可能であれば、値に対して Dictionary または SortedList を試すことをお勧めします。どちらかがルックアップのパフォーマンスをわずかに向上させると思います。

于 2012-04-20T23:09:52.053 に答える