14

我刚开始在一个非常小的 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

$nums数据集在哪里DB[:numbers]

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

应该怎么做?

4

4 回答 4

20

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

  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
于 2012-03-28T14:00:43.473 回答
16

insert_conflict为 Postgres v9.5添加了 Sequel 4.25.0(2015 年 7 月 31 日发布) +为 SQLite
添加了 Sequel 4.30.0(2016 年 1 月 4 日发布)insert_conflict

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

DB[:table_name].insert_conflict(:update).insert( number:n, type:t, counter:c )
于 2016-01-19T22:28:22.037 回答
3

我相信你不能让它比这更干净(尽管某些数据库具有特定的 upsert 语法,这可能受 Sequel 支持)。你可以把你拥有的东西包装在一个单独的方法中,并假装它不存在。:)

只是几个建议:

  • 将所有内容包含在事务中。
  • (number, type)在字段上创建唯一索引。
  • 不要使用全局变量。
于 2012-03-19T11:40:44.353 回答
1

您可以使用upsert,但它目前不适用于更新计数器。希望未来的版本会 - 欢迎提出想法!

于 2012-07-05T16:14:23.430 回答