我需要为我的模型生成一个唯一的数字,称为Shipment
。
该号码必须采用以下格式:
例如:1114000001。11 - 当前月份。 14年。 0000001 - 只有六位数。
因此。我的想法是使用6位数的计数器。我想将它存储在数据库中,然后我可以使用lock
并确保唯一性。但很明显,这是一个潜在的瓶颈。因为每个新的shipment
都会排队等候访问counter
。
我的问题是:还有什么更好的?使用计数器或生成随机数,并查询数据库:
while Shipment.where(:id => supposedly_unique_number.).exists?
还有一个问题:我在哪里存储这个ounter?我只需要一个用于整个应用程序。仅为一个值创建独立表?
更新
我刚才意识到这个检查
Shipment.where(:id => supposedly_unique_number.).exists?
不保证唯一性。 :)
那么哪里可以更好地存放柜台? 我很高兴你的任何建议。
UPDATE2
mu太短,提到了数据库序列。 而且我找不到可以理解的信息如何使用它。 如果有人向我解释它是如何运作的,我将不胜感激。
Rails 4,Ruby 2,PostgreSql。
答案 0 :(得分:1)
这里有几件事你需要考虑:
id
将有前导零,因此它们不是数字,它们是包含数字字符的字符串。id
。使用id
的字符串是一项工作,以防止ActiveRecord认为它拥有你但不是那么糟糕。您需要创建没有id
添加的隐式create_table
的表,添加您自己的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)
提前序列并返回新值
再次使用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