【问题标题】:How to update or insert on Sequel dataset?如何在 Sequel 数据集上更新或插入?
【发布时间】:2012-03-19 11:06:29
【问题描述】:

我刚开始在一个非常小的 Sinatra 应用程序中使用 Sequel。由于我只有一个 DB 表,因此我不需要使用模型。

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

  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

其中$numsDB[:numbers] 数据集。

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

应该怎么做?

【问题讨论】:

标签: ruby upsert sequel


【解决方案1】:

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

  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

【讨论】:

  • 这是一个不错的解决方案。谢谢
  • 这个解决方案仍然引入了竞态条件的可能性。如果两个并行进程/线程在其中一个到达插入(第 3 行)之前执行更新(第 2 行),则将插入两条记录。考虑使用互斥锁、数据库锁或适当的事务策略。
  • Flexoid:你是对的,下面的解决方案 - 基本上“将所有内容放入事务中”是正确的。尽管如此,当“UPDATE + INSERT”足够时,“SELECT、UPDATE、INSERT”命令的顺序是没有意义的。 (当然是事务。)事务的有趣之处:如果两个并行运行的事务递增相同的计数器,您仍然会遇到问题。
【解决方案2】:

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

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

DB[:table_name].insert_conflict(:update).insert( number:n, type:t, counter:c )

【讨论】:

    【解决方案3】:

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

    只是几个建议:

    • 将所有内容包含在事务中。
    • (number, type) 字段上创建唯一索引。
    • 不要使用全局变量。

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2019-12-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-04-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多