如何在Sequel数据集上更新或插入?

noo*_*oga 12 ruby upsert sequel

我刚开始在一个非常小的Sinatra应用程序中使用Sequel.由于我只有一个数据库表,所以我不需要使用模型.

我想更新记录(如果存在)或插入新记录(如果不存在).我提出了以下解决方案:

  rec = $nums.where(:number => n, :type => t)
  if $nums.select(1).where(rec.exists)
    rec.update(:counter => :counter + 1)
  else
    $nums.insert(:number => n, :counter => 1, :type => t)
  end
Run Code Online (Sandbox Code Playgroud)

哪里$numsDB[:numbers]数据集.

我相信这种方式不是"更新或插入"行为的最优雅实现.

应该怎么做?

rad*_*iel 17

在更新/插入之前你可能不应该检查; 因为:

  1. 这是一个额外的数据库调用.
  2. 这可能会引入竞争条件.

你应该做的是测试更新的返回值:

rec = $nums.where(:number => n, :type => t)
if 1 != rec.update(:counter => :counter + 1)
  $nums.insert(:number => n, :counter => 1, :type => t)
end
Run Code Online (Sandbox Code Playgroud)

  • 这个解决方案仍然引入了竞争条件的可能性。如果两个并行进程/线程在其中一个到达插入(第 3 行)之前执行更新(第 2 行),则将插入两条记录。考虑使用互斥锁、数据库锁或适当的事务策略之类的东西。 (2认同)

Phr*_*ogz 11

续集4.25.0(2015年7月31日发布)insert_conflict
Postite添加了适用于SQLite的Postgres v9.5 +续集4.30.0(2016年1月4日发布)insert_conflict

这可以用于插入或更新行,如下所示:

DB[:table_name].insert_conflict(:update).insert( number:n, type:t, counter:c )
Run Code Online (Sandbox Code Playgroud)