如何在ActiveRecord中实现PostgreSQL的`upsert`功能?

3
我有一串数据,其中包含类别。我希望维护一个类别表,该表应该仅包含我遇到的每个类别一次。
我想实现一个 id = Category.upsert(name),它应该是原子性的(当然),并且如果可能的话不使用存储过程在数据库端。
3个回答

1
这个怎么样:
class Category < ActiveRecord::Base
  ...

  class << self
    def upsert(name)
      transaction { self.find_or_create_by(name: name).id }
    end
  end
end

不错,但就我所知 - 它需要两个往返到数据库... 你知道有没有办法只进行一次往返吗? - Uri Agassi
2
你说得对,确实如此,但据我所知,如果没有存储过程,就无法做得更好。 - lacrosse

1

听起来很有趣,但它使用存储过程,并且似乎没有返回我需要的 id/对象... - Uri Agassi

-2
你需要编写一个 ActiveRecordExtension。可以查看this
代码大致如下:
module ActiveRecordExtension
  extend ActiveSupport::Concern

  def upsert(attributes)
    begin
        create(attributes)
    rescue ActiveRecord::RecordNotUnique, PG::UniqueViolation => e
        find_by_primary_key(attributes['primary_key']).
        update(attributes)
    end
  end
end

ActiveRecord::Base.send(:include, ActiveRecordExtension)

这个解决方案几乎不是原子性的 - 它甚至没有进行两次往返 - 而是进行了三次... - Uri Agassi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接