我有一个Web方法,可将一堆食谱插入数据库的队列中(以存储用户对烹饪感兴趣的食谱,类似于NetFlix的电影队列)。用户能够立即检查一堆食谱并将其排队。我有与此类似的代码:
[WebMethod] public void EnqueueRecipes(SecurityCredentials credentials, Guid[] recipeIds) { DB.User user = new DB.User(credentials); using (new TransactionScope(OnDispose.Commit)) { foreach (Guid rid in recipeIds) { DB.QueuedRecipe qr = new DB.QueuedRecipe(Guid.NewGuid(), user, new DB.Recipe(rid)); qr.Create(); } } }
我对UserId / RecipeId有一个唯一的约束,因此用户只能将一个配方入队。但是,如果他们碰巧选择了已经在队列中的食谱,那么我真的不想用错误消息打扰用户,我只想忽略该食谱。
如果违反唯一约束,则上面的代码将引发SQL异常。解决此问题的最佳方法是什么,而只是忽略重复的行。我目前的想法是:
continue
Castle或NHibernate框架还有其他小技巧吗?另外,我的SQL后端是PostgreSQL 9.0。谢谢!
更新:
我尝试了第一种方法,它看起来效果很好。在我看来,我不必加载整个队列,只需要加载那些出现在recipeIds中的队列即可。我相信我的foreach()循环现在是O(n ^ 2),具体取决于的效率,List<Guid>::Contains()但是我认为这对于我将要使用的大小可能是不错的。
foreach()
List<Guid>::Contains()
//Check for dupes DB.QueuedRecipe[] dbRecipes = DB.QueuedRecipe.FindAll(Expression.In("Recipe", (from r in recipeIds select new DB.Recipe(r)).ToArray() )); List<Guid> existing = (from r in dbRecipes select r.Recipe.RecipeId).ToList(); using (new TransactionScope(OnDispose.Commit)) { foreach (Guid rid in recipeIds) { if (existing.Contains(rid)) continue; DB.QueuedRecipe qr = new DB.QueuedRecipe(Guid.NewGuid(), user, new DB.Recipe(rid)); qr.Create(); } }
您可以使用一条SQL语句来做到这一点:
INSERT INTO user_recipe SELECT new_UserId, new_RecipeId FROM user_recipe WHERE NOT EXISTS ( SELECT * FROM user_recipe WHERE (UserId, RecipeId) = (new_UserId, new_RecipeId) );
该SELECT只返回该行,如果它不存在,所以它只会在这种情况下插入。
SELECT
如果您要一次性插入一长串食谱,则可以:
CREATE TEMP TABLE i(userId int, recipeid int) ON COMMIT DROP; INSERT INTO i VALUES (1,2), (2,4), (2,4), (2,7), (2,43), (23,113), (223,133); INSERT INTO user_recipe SELECT DISTINCT i.* -- remove dupes from the insert candidates themselves FROM i LEFT JOIN user_recipe u USING (userid, recipeid) WHERE u.userid IS NULL;
迈克(Mike)评论说,临时表对于仅几条记录来说是一个过大的杀伤力。
INSERT INTO user_recipe SELECT i.* FROM ( SELECT DISTINCT * -- only if you need to remove possible dupes FROM ( VALUES (1::int, 2::int) ,(2, 3) ,(2, 4) ,(2, 4) -- dupe will be removed ,(2, 43) ,(23, 113) ,(223, 133) ) i(userid, recipeid) ) i LEFT JOIN user_recipe u USING (userid, recipeid) WHERE u.userid IS NULL;