如何在 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。
参见:
我创建了 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。
参见: