Sql update web2py更新\u或插入以增加计数器

Sql update web2py更新\u或插入以增加计数器,sql-update,counter,sql-insert,web2py,Sql Update,Counter,Sql Insert,Web2py,我有一个web2py定义的数据库表,用于计算特定用户发出请求的次数,因此它有整数列“user\u id”和“n\u req”。我不想为每个可能的用户填充一个零。相反,我想检查是否存在一个具有user_id的行,如果存在,则将'n_req'增加1,否则使用'n_req'的初始值1创建它。为了避免竞争条件,我想使用单个update_或insert调用来实现这一点,例如 db.count_table.update(db.count_table.user_id == uid, user_id = uid

我有一个web2py定义的数据库表,用于计算特定用户发出请求的次数,因此它有整数列“user\u id”和“n\u req”。我不想为每个可能的用户填充一个零。相反,我想检查是否存在一个具有user_id的行,如果存在,则将'n_req'增加1,否则使用'n_req'的初始值1创建它。为了避免竞争条件,我想使用单个update_或insert调用来实现这一点,例如

db.count_table.update(db.count_table.user_id == uid, user_id = uid, n_req = n_req + 1)

但是,我假设我不能这样做,因为它在递增时使用了预先存在的
n_req
值。那么如何告诉DAL
n_req
的初始值呢。例如,
n|req=(n|req | 0)+1

在这种情况下,我认为您不能使用
.update\u或\u insert
方法,因为
n|rec
的值取决于是否找到记录。相反,您可以这样做:

db.define_table('count_table',
    Field('user_id', 'integer', unique=True),
    Field('n_rec', 'integer', default=1))

def update_count(user_id):
    return db(db.count_table.user_id == user_id).update(n_rec=db.count_table.n_rec + 1)

if not update_count(uid):
    try:
        db.count_table.insert(user_id=uid)
    except db._adapter.driver.IntegrityError:
        update_count(uid)
请注意,
.update
中的
n_rec
的值设置为
db.count_table.n_rec+1
,这将转换为SQL语句,该语句将使数据库增加现有值,而不是自己显式提供最终值。在两个请求同时更新计数的情况下,这应该避免争用条件


另外,请注意,
user\u id
字段上有一个
unique
约束。这将防止竞争条件允许为同一用户创建两个记录。在这种情况下,
try/except
将捕获生成的
IntegrityError
,并对现有记录进行更新。

谢谢@anthony。如果在第一个'db.count_table'调用和'insert'之间,另一个进程添加了行,会发生什么情况。我想这会失败的,所以我们希望能找到一种原子化的方法。很好。上面的代码将避免更新时的竞争条件,但您仍然可以在初始插入时获得竞争条件。为了避免这个问题,您应该在
用户id
字段上设置一个
唯一的
约束,以防止应用程序为同一用户创建两个记录。然后,您可以将
insert
包装在
try/except
中,以捕获违反唯一约束的行为。如果出现这样的异常,请再次检查现有记录,并进行更新而不是插入。我更新了答案以处理插入时的竞争条件。谢谢@Anthony。真遗憾,这一切都这么复杂!我有点希望有一个简单的一行解决方案。但我想这就是你的比赛条件。