如何在 ralis 中维护相同 table 中的不同计数器

how to maintain different counter in same table in ralis

我创建了 invoice 模型,该模型具有属性 invoice_type,其值类似于 tax_invoice 的 1 和 proforma_invoice 的 2。现在我想在同一个 table 中维护单独的唯一增量计数器。我如何在 rails 5 应用程序中执行此操作。

我当前的模型代码如下所示:

 def set_invoice_number
   self.update_attribute(:invoice_number, 'abc/2020-21/' + id.to_s)
 end

示例: 对于 1 类发票,我想维护计数器 1、2、3.... 基于之前创建的 1 类发票。

对于 2 类发票,我想维护计数器 1、2、3.... 基于之前创建的 2 类发票。

我想以 'abc/2020-21/' + id.to_s 格式附加 ID 并保存到 invoice_number 属性中的发票 table。

我认为与其依赖于 id,不如在数据库中创建一个新的 运行 号码。

那么保存的时候要取发票类型前面的number

def set_invoice_number
  next_invoice_number = Invoice.where(invoice_type: 1).maximum(:number).to_i + 1
  
  self.update_attributes(
   invoice_number: 'abc/2020-21/' + next_invoice_number.to_s,
   number: next_invoice_number
  )
end

to_i处理没有前面数字的情况。

id由底层数据库的主键序列生成table。它是一个计数器,而不是 table 中的行数。记住这一点非常重要,因为如果某行被删除,使用当前行的计数可能会导致重复数字!

像 postgres 这样的一些数据库允许你定义自定义序列,基本上只是一个 table 上面有一个计数器:

CREATE SEQUENCE tax_invoice_serial START 1;
CREATE SEQUENCE proforma_invoice_serial START 1;

然后您可以通过从数据库序列中获取下一个值来分配 invoice_number:

self.update("invoice_number = CONCAT('abc/2020-21/', nextval('tax_invoice_serial'))")

要select序列动态使用case语句:

self.update(
  <<~SQL 
    name = (
      CASE 
        WHEN invoice_type = 1 THEN CONCAT('abc/2020-21/', nextval('tax_invoice_serial'));
        WHEN invoice_type = 2 THEN CONCAT('abc/2020-21/', nextval('proforma_invoice_serial'));
      END
    )
  SQL
})

由于整个序列生成发生在数据库中,因此它不像基于 Ruby 的解决方案那样容易出现竞争条件。当然你也可以使用 database trigger 来处理这个而不是模型回调。

在其他数据库上,例如 MySQL 和 SQLite,只有 AUTO_INCREMENT 我会考虑其他替代方案,例如将其分成两个单独的 tables。

参见: