Rails 中的 upsert 函数是如何工作的?

Ale*_*lex 4 ruby-on-rails rails-activerecord

由于 upsert 是在 Rails 的 ActiveRecord 中实现的“较新”功能,因此它背后的文档并不多。我确实理解它背后的概念,因为它的目的是如果它是一条新记录,则插入一条新记录,如果在数据库中找到它,则更新现有记录。

例如,当我调用: 时Model.upsert(name: 'a', number: 1),ActiveRecord 是查找名称为“a”的预先存在的记录还是编号为 1 的预先存在的记录?

For example, right now when I am trying to update a Model with the name a using Model.upsert(name: 'a', number: 1), I get a NotNullViolation, because there is a null value in an id that I am not specifying. Can I update a model with upsert without passing in the id as a parameter?

Chr*_*ald 7

根据目标数据库的不同,实现会有所不同。例如,Postgres 通过INSERT...ON CONFLICT UPDATE 原生支持更新插入。您应该参考底层数据库的文档以了解详细信息。但是,一般而言,ActiveRecord 将尝试使用表上的所有唯一索引来确定现有记录是否冲突。至少这意味着您的主键,尽管它也可能意味着其他键。如果您使用的是 Postgres 或 SQLite,则可以传递:unique_by给 upsert 以明确指示应使用哪些字段来确定匹配记录是否已存在 ( doc )。请注意,如果您使用它并尝试插入与未列出的唯一键上的另一记录冲突的记录unique_by,您的数据库将抛出异常。

我得到一个 NotNullViolation,因为在我没有指定的 id 中有一个空值。

不,当然不是 - 您必须在执行 upsert 时指定将在插入期间插入的所有字段,因为显然,如果记录不存在且必须插入,则它必须包含所有必需的字段。如果您将字段指定为非空,则当插入的值为空时,您不能尝试对它们进行 upsert!

由于您没有传递 ID,Rails 无法弄清楚如何确定记录的唯一性。它可以与您的其他字段之一上的唯一索引一起使用(假设唯一索引适合您的应用程序)。

  • “将是唯一的”和“通过唯一索引强制唯一”是两个不同的东西。您将需要在名称列上有一个唯一索引,然后您应该能够使用“upsert(..., unique_by: :name)”进行更新插入,假设您的目标是受支持的数据库。 (2认同)