31

Entity Framework Code First で ASP.NET MVC4 を使用しています。主キー「UserId」を持つ「users」というテーブルがあります。このテーブルには 200,000 以上のエントリがある場合があります。

さらに 50 人のユーザーを挿入する必要があります。私はこれを次のようにするかもしれません

foreach(User user in NewUsers){
    context.Add(user);
}
dbcontext.SaveChanges();

問題は、これらの新しいユーザーの 1 人以上が DB に既に存在している可能性があることです。それらを追加してから保存しようとすると、エラーがスローされ、有効なものはどれも追加されません。これを行うためにコードを変更できます。

foreach(User user in NewUsers){
    if(dbcontext.Users.FirstOrDefault(u => u.UserId) == null)
    {
        dbcontext.Users.Add(user);
    }
}
dbcontext.SaveChanges();

これはうまくいくでしょう。問題は、200,000 以上のエントリ テーブルに対してクエリを 50 回実行する必要があることです。だから私の質問は、重複を無視して、これらのユーザーを挿入する最もパフォーマンス効率の良い方法は何ですか?

4

5 に答える 5

15

あなたはこれを行うことができます:

var newUserIDs = NewUsers.Select(u => u.UserId).Distinct().ToArray();
var usersInDb = dbcontext.Users.Where(u => newUserIDs.Contains(u.UserId))
                               .Select(u => u.UserId).ToArray();
var usersNotInDb = NewUsers.Where(u => !usersInDb.Contains(u.UserId));
foreach(User user in usersNotInDb){
    context.Add(user);
}

dbcontext.SaveChanges();

これにより、データベースで単一のクエリが実行され、既存のユーザーが検索され、NewUsersセットから除外されます。

于 2013-08-07T20:48:48.637 に答える
3

1 つのクエリで既存のユーザーを除外できます

foreach(User user in NewUsers.Where(us => !dbcontext.Users.Any(u => u.userId == us.userId)))
{
    dbcontext.Users.Add(user);
}
dbcontext.SaveChanges();

編集:

コメントで指摘されているように、上記の提案により、NewUsers コレクション内の各要素に対して sql 呼び出しが行われます。SQL Server Profiler でそれを確認できました。

プロファイリングの興味深い結果の 1 つは、項目ごとに EF によって生成されたやや奇妙な sql です (モデル名は OP とは異なりますが、クエリは同じです)。

exec sp_executesql N'SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[EventGroup] AS [Extent1]
    WHERE [Extent1].[EventGroupID] = @p__linq__0
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[EventGroup] AS [Extent2]
    WHERE [Extent2].[EventGroupID] = @p__linq__0
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]',N'@p__linq__0 int',@p__linq__0=10

シンプルなワンライナーの仕事をするための非常に素晴らしいコードです。

私の見解では、適切で読みやすい宣言型コードを作成し、コンパイラーとオプティマイザーに汚い仕事を任せることは素晴らしい姿勢です。これは、そのようなスタイルの結果が驚くべきものであり、汚れなければならない場合の 1 つです。

于 2013-08-07T20:52:04.827 に答える
3

これは主キーであるため、選択肢は限られています。これがプライマリ キーではなく、SQL Server を想定した単なる一意のインデックスである場合、重複を無視するように一意のキーを設定できます。

私が提案するのは、単純に Add を try/catch でラップし、例外が重複キー エラーである場合は例外を食べることです。

AddOrUpdate()オブジェクトがメソッドをサポートしているかどうかも確認できます。これが Code First 実装でサポートされていることは知っています。この場合、行が存在する場合は新規または更新の追加が行われると思います。ただし、これには、追加または更新を行うかどうかを知るために、ユーザーが既に存在するかどうかを確認するために DB へのトリップが必要になる場合があります。また、実際には更新を実行したくない場合もあります。

私だったら、Try/Catch のルートに行くと思います。

于 2013-08-07T20:48:26.533 に答える