我需要为我的模型生成一个唯一编号,称为 Shipment
。
该号码必须采用以下格式:
- 两位数 - 当前月份
- 两位数 - 当前年份
- 六位数 - 只是数字
例如:1114000001。11 - 当前月份。 14 岁。 0000001 - 只有六位数。
所以。我的想法是使用 6 位数的计数器。我想将它存储在数据库中,然后我可以使用 lock
并确保唯一性。但很明显,这是一个潜在的瓶颈。因为每一个新的shipment
都会排队等待访问counter
。
我的问题是:什么更好?使用计数器或生成一个随机数,并查询数据库:
while Shipment.where(:id => supposedly_unique_number.).exists?
还有一个问题:我该把计数器存放在哪里?整个申请我只需要一个。仅为一个值创建独立表?
更新
我刚刚意识到这张支票
Shipment.where(:id => supposedly_unique_number.).exists?
不保证唯一性。 :)
那么柜台存放在哪里比较好? 我很乐意听取您的任何建议。
更新 2
mu 太短,提到数据库序列。 而且我找不到如何使用它的可理解信息。 如果有人向我解释它是如何工作的,我将不胜感激。
Rails 4、Ruby 2、PostgreSql。
最佳答案
这里有几件事你需要考虑:
- 您的
id
将有前导零,因此它们不是数字,它们是包含数字字符的字符串。 - 您应该让数据库负责计数器。任何你试图在 Ruby 中做的事情都将是一团乱七八糟的困惑和竞争条件。同样,您应该让数据库负责构建
id
。
使用字符串作为 id
可以防止 ActiveRecord 认为它拥有您,但还不错。您需要创建没有create_table
添加的隐式id
的表,添加您自己的id
,并手动将其设置为主键:
create_table :shipments, :id => false do |t|
# Don't bother with `t.string` when using PostgreSQL, it is pointless.
t.text :id, :null => false
...
end
connection.execute('alter table shipments add primary key (id)')
现在我们需要连接一个默认值,以便数据库可以生成我们的 id
。获取日期前缀很简单,使用 to_char
SQL function格式化当前时间(now()
):
to_char(now(), 'MMYY')
要创建后缀,我们可以使用 sequence .数据库序列只是数据库对象,它在存在多个数据库连接的情况下以安全的方式返回递增的数字。首先,创建序列:
connection.execute('create sequence shipments_id_seq owned by shipments.id')
要从序列中获取下一个值,我们使用 nextval
SQL function :
nextval(regclass)
Advance sequence and return new value
然后再次使用 to_char
格式化它:
to_char(nextval('shipments_id_seq'), 'FM000000')
linked document解释了 FM000000
格式的含义。将 id
的这两部分组合起来,我们得到一个默认值:
to_char(now(), 'MMYY') || to_char(nextval('shipments_id_seq'), 'FM000000')
请注意,||
是 SQL 中的字符串连接。将其包装在另一个 connection.execute
中让我们将默认值附加到 id
列:
connection.execute(%q{
alter table shipments
alter column id
set default to_char(now(), 'MMYY') || to_char(nextval('shipments_id_seq'), 'FM000000')
})
如果您想在每个月的月初(或接近六位数限制时)重置计数器,您可以使用 setval
SQL function .
最后,由于您正在使用 ActiveRecord 无法理解的各种东西,因此您需要从 schema.rb
切换到 structure.sql
管理您的架构信息。您可以通过设置在配置中执行此操作:
config.active_record.schema_format = :sql
并且您将使用 rake db:structure:dump
和 db:structure:load
而不是模式任务。
将所有这些放在一起将为您提供如下迁移:
def up
create_table :shipments, :id => false do |t|
# Don't bother with `t.string` when using PostgreSQL, it is pointless.
t.text :id, :null => false
...
end
connection.execute('alter table shipments add primary key (id)')
connection.execute('create sequence shipments_id_seq owned by shipments.id')
connection.execute(%q{
alter table shipments
alter column id
set default to_char(now(), 'MMYY') || to_char(nextval('shipments_id_seq'), 'FM000000')
})
end
关于ruby-on-rails - Rails 4. 唯一 ID 计数器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26943277/