Mik*_*sen 7 c# sql postgresql nhibernate castle-activerecord
我有一个webmethod,它将一堆食谱插入到数据库的队列中(以存储用户有兴趣烹饪的食谱,类似于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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我对UserId/RecipeId有一个独特的约束,因此用户只能将配方排队一次.但是,如果他们碰巧选择了已经在队列中的配方,我真的不想用错误消息打扰用户,我只想忽略该配方.
如果违反了唯一约束,上面的代码将抛出SQL异常.解决这个问题的最佳方法是什么,只需忽略重复的行.我目前的想法是:
continue在for循环中.优点:没有不必要的SQL插入发送到数据库.缺点:较慢,特别是如果用户有一个大队列.还有Castle或NHibernate框架的其他小技巧吗?另外,我的SQL后端是PostgreSQL 9.0.谢谢!
更新:
我采取了第一种方法,它看起来效果很好.它发生在我身上我不必加载整个队列,只需加载出来在recipeIds中的队列.我相信我的foreach()循环现在是O(n ^ 2)取决于效率,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();
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用一条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)
);
Run Code Online (Sandbox Code Playgroud)
该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;
Run Code Online (Sandbox Code Playgroud)
迈克(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;
Run Code Online (Sandbox Code Playgroud)