0

この質問にはおそらく10個の重複がありますが、現在これを行っているよりも良い方法があるかどうか知りたいです. これは、違いを判断する方法を示すために使用している小さな例です。

        //let t1 be a representation of the ID's in the database.
        List<int> t1 = new List<int>() { 5, 6, 7, 8 };
        //let t2 be the list of ID's that are in memory.
        //these changes need to be reflected to the database.
        List<int> t2 = new List<int>() { 6, 8, 9, 10 };

        var hash = new HashSet<int>(t1);
        var hash2 = new HashSet<int>(t2);
        //determines which ID's need to be removed from the database
        hash.ExceptWith(t2); 
        //determines which ID's need to be added to the database.
        hash2.ExceptWith(t1);

        //remove contents of hash from database
        //add contents of hash2 to database

現在実行しなければならない 2 つの操作ではなく、1 つの操作で何を追加および削除するかを判断できるかどうかを知りたいです。この操作のパフォーマンスを向上させる方法はありますか? 実際のデータベースの状況では、数十万の ID があることに注意してください。

EDITまたは2番目の質問です。データベースで直接実行できるLINQクエリがあるので、IDの新しいリストを提供して、それ自体を自動的に削除/追加できますか? (mysqlを使用)

明確化 2 つの SQL クエリ (またはストアド プロシージャ) が必要であることはわかっています。問題は、リスト内の違いを 1 回のアクションで判断できるかどうか、およびこれよりも速く実行できるかどうかです。

EDIT2

SPFiredrake からのこの操作は、私のハッシュセット バージョンよりも高速であるように見えますが、データベースに追加するものと削除するものを決定する方法がわかりません。その情報を操作に含める方法はありますか?

t1.Union(t2).Except(t1.Intersect(t2))

EDIT3

気にしないでください、このステートメントには実際には実行が遅れるという問題があることを忘れていましたが、誰かが疑問に思っている場合に備えて、カスタム比較子と追加された変数を使用して、それがどのリストからのものであるかを決定することで、以前の問題を解決しました。

4

2 に答える 2

1

最終的には、完全な外部結合 (LINQ の世界では 2 つの GroupJoin) を使用します。ただし、どちらのテーブルにも一致するレコードがない値のみを考慮します。NULL 右値 (左外部結合) は削除を示し、NULL 左値 (右外部結合) は追加を示します。したがって、このように機能させるには、2 つの左外部結合 (右外部結合をエミュレートするために 2 番目のケースの入力を切り替える) を実行し、それらを連結します (ユニオンを使用できますが、取り除くので不要です)。とにかく重複)。

List<int> t1 = new List<int>() { 5, 6, 7, 8 };
List<int> t2 = new List<int>() { 6, 8, 9, 10 };

var operations = 
    t1.GroupJoin(
        t2, 
        t1i => t1i, 
        t2i => t2i, 
        (t1i, t2join) => new { Id = t1i, Action = !t2join.Any() ? "Remove" : null })
    .Concat(
        t2.GroupJoin(
            t1, 
            t2i => t2i, 
            t1i => t1i, 
            (t2i, t1join) => new { Id = t2i, Action = !t1join.Any() ? "Insert" : null })
    .Where(tr => tr.Action != null)

これにより、選択ステートメントが得られます。次に、このデータをストアド プロシージャにフィードして、テーブルに既に存在する値を削除し、残り (または削除と追加を実行する 2 つのリスト) を追加します。いずれにせよ、まだ最もクリーンな方法ではありませんが、少なくともこれで考えさせられます。

編集: 私の最初の解決策は、必要なアクションに基づいて 2 つのリストを分離することでした。ワンライナーを使用して同じことを行うことができますが (ただし、どのアクションを実行するかは気にしません)、同じ問題に悩まされることになると思います (Hashsets [ハッシュ コレクション] ではなく LINQ [列挙型] を使用します)。

// XOR of sets = (A | B) - (A & B), - being set difference (Except)
t1.Union(t2).Except(t1.Intersect(t2))

ハッシュセットを使用するよりもまだ遅くなると思いますが、とにかく試してみてください。

編集:はい、コレクションを列挙するまで実際には何もしないため、より高速です(foreachで、または具体的なデータ型に取得することによって[IE:List<>、Arrayなど]) . 追加/削除するものを整理するにはまだ余分な時間がかかり、それが最終的な問題です。2 つのクエリを分割することで同等の速度を得ることができましたが、メモリ内の世界に (ToList() を介して) 取得すると、ハッシュセット バージョンよりも遅くなりました。

t1.Except(t2); // .ToList() slows these down
t2.Except(t1); 

正直なところ、SQL側で処理します。ストアド プロシージャでは、(値がテーブルに既に存在するかどうかに基づいて) 追加または削除を示す別の列を使用して、すべての値をテーブル変数に格納します。次に、このテーブル変数に結合して、一括削除/挿入を行うことができます。

編集:完全なリストをデータベースに送信し、sprocで処理することで、私が意味することを拡張したいと思いました:

var toModify = t1.Union(t2).Except(t1.Intersect(t2));
mods = string.Join(",", toModify.ToArray());
// Pass mods (comma separated list) to your sproc.

次に、ストアド プロシージャで次のようにします。

-- @delimitedIDs some unbounded text type, in case you have a LOT of records
-- I use XQuery to build the table (found it's faster than some other methods)
DECLARE @idTable TABLE (ID int, AddRecord bit)
DECLARE @xmlString XML
SET @xmlString = CAST('<NODES><NODE>' + REPLACE(@delimitedIDs, ',', '</NODE><NODE>') + '</NODE></NODES>' as XML)

INSERT INTO @idTable (ID)
SELECT node.value('.','int') 
FROM @xmlString.nodes('//NODE') as xs(node)

UPDATE id
SET AddRecord = CASE WHEN someTable.ID IS NULL THEN 1 ELSE 0 END
FROM @idTable id LEFT OUTER JOIN [SomeTable] someTable on someTable.ID = id.ID

DELETE a
FROM [SomeTable] a JOIN @idTable b ON b.ID = a.ID AND b.AddRecord = 0

INSERT INTO [SomeTable] (ID)
SELECT id FROM @idTable WHERE AddRecord = 1

確かに、これは ID を挿入するだけで、実際には他の情報を追加しません。ただし、XML データを sproc に渡し、同様の方法で XQuery を使用して、追加する必要がある情報を取得することはできます。

于 2012-05-24T15:42:24.350 に答える
0

Linq バージョンに置き換えても、2 つの操作が必要です。

純粋な SQL を使用してこれを行っていると仮定しましょう。

おそらく次の 2 つのクエリが必要です。

  • レコードを削除するための 1 つ
  • それらを追加するための別のもの

LINQ コードを使用すると、ソリューションよりもはるかに複雑になり、読みにくくなります。

于 2012-05-24T15:41:37.393 に答える