【问题标题】:Upsert in Rails ActiveRecordRails ActiveRecord 中的更新插入
【发布时间】:2011-06-09 09:29:39
【问题描述】:

ActiveRecord 有内置的 upsert 功能吗?我知道我可以自己写,但如果这样的东西已经存在,我显然不想写。

【问题讨论】:

    标签: ruby-on-rails activerecord upsert


    【解决方案1】:

    rails 6 中有一个很棒的新功能:他们将 upsertupsert_all 添加到 ActiveRecord

    更多信息可以在这里找到https://edgeapi.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-upsert_all

    【讨论】:

    • 这应该谨慎使用,因为 upsert 和 upsert_all 都会绕过所有验证和回调。
    • 加个例子说明如何使用可能?
    【解决方案2】:

    Model.find_or_initialize 可能会做你想做的事。如果有意义的话,你可以用saveupdate_attributes 链接它。

    更多信息请关注Rails Guides

    【讨论】:

    • 查看对 Pasta 回答的评论
    • 有没有人看到这会产生 upsert? rails 指南表明新对象还不会存储在数据库中,所以我看不出这是一个真正的数据库 upsert。即,无法在多线程环境中可靠地工作。
    • 此解决方案存在并发问题。如果另一个线程更新find_or_initializesave 之间的表,它将失败。
    • 这不是一个 upsert。在结果方面,它是等价的(只要不会发生像@BarryFruitman 提到的并发问题),但在性能方面则不然。
    【解决方案3】:

    我刚刚遇到了这个库: https://github.com/seamusabshere/upsert

    我还没有测试过,但看起来很有希望

    【讨论】:

      【解决方案4】:

      IMO Upsert 机制需要为每个模型进行自定义配置。

      因此,最好的解决方案是为模型实现自定义 SQL 查询,例如

      insert into <table> (<field_1>, ..., <field_n>) 
        values (<field_1_value>, ..., <field_n_value>)
      on duplicate key update
        field_x = field_x_value,
        ...
        field_z = field_z_value;
      

      【讨论】:

        【解决方案5】:

        Rails 6 针对这种情况引入了 create_or_find_by https://github.com/rails/rails/pull/31989

        对于大量记录,也可以使用https://github.com/zdennis/activerecord-import

        例子:

        Book.import [book], on_duplicate_key_update: [:title]
        

        【讨论】:

          【解决方案6】:

          还有Model.find_or_create

          【讨论】:

          • 这不会做一个 upsert。它进行选择,然后(可选)插入。虽然您在单线程世界中获得相同的效果,但在多线程世界中,您需要它来执行实际的 upsert。
          【解决方案7】:

          我写了一篇关于我们如何实现这一目标的博文。看看here

          您必须编写一个活动记录扩展。它看起来像这样。

          module ActiveRecordExtension
            extend ActiveSupport::Concern
          
            def self.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)
          

          【讨论】:

          • 不喜欢使用错误来驱动预期的逻辑流程,尤其是当您可以预测何时会发生此错误时。
          • 对于那些投反对票的人,这是一个standard approach in Rails 6,虽然包含在交易中。
          猜你喜欢
          • 2012-07-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多