如何在 Sequel 数据集上更新或插入?
Posted
技术标签:
【中文标题】如何在 Sequel 数据集上更新或插入?【英文标题】:How to update or insert on Sequel dataset? 【发布时间】: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
其中$nums
是DB[:numbers]
数据集。
我相信这种方式并不是“更新或插入”行为的最优雅的实现方式。
应该怎么做?
【问题讨论】:
***.com/questions/3647454/… 【参考方案1】:您可能不应该在更新/插入之前检查;因为:
-
这是一个额外的数据库调用。
这可能会引入竞争条件。
你应该做的是测试更新的返回值:
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,但它目前不适用于更新计数器。希望未来的版本能够 - 欢迎提出想法!
【讨论】:
以上是关于如何在 Sequel 数据集上更新或插入?的主要内容,如果未能解决你的问题,请参考以下文章