将范围的自动增量添加到migration-file rails中的现有列

时间:2013-06-10 12:58:20

标签: ruby-on-rails postgresql migration auto-increment database-migration

我的数据库中有帖子和组织。帖子属于组织和组织has_many帖子。

我的帖子表中有一个现有的post_id列,我现在在创建新帖子时手动递增。 如何将自动增量添加到作用于organisation_id的列?

目前我使用mysql作为我的数据库,但我打算切换到PostgreSQL,因此如果可能,解决方案应该适用于两者:)

非常感谢!

4 个答案:

答案 0 :(得分:5)

@ richard-huxton有正确的答案并且是线程安全的。

使用事务块并在该事务块内使用SELECT FOR UPDATE。这是我的rails实现。在ruby类上使用'transaction'来启动事务块。在要锁定的行上使用“lock”,实质上阻止对该行的所有其他并发访问,这是确保唯一序列号所需的。

class OrderFactory
  def self.create_with_seq(order_attributes)
    order_attributes.symbolize_keys!
    raise "merchant_id required" unless order_attributes.has_key?(:merchant_id)
    merchant_id = order_attributes[:merchant_id]

    SequentialNumber.transaction do
      seq = SequentialNumber.lock.where(merchant_id: merchant_id, type: 'SequentialNumberOrder').first
      seq.number += 1
      seq.save!
      order_attributes[:sb_order_seq] = seq.number
      Order.create(order_attributes)
    end
  end
end

我们为后台工作运行sidekiq,因此我通过创建1000个后台作业来测试此方法,以使用8个工作程序创建订单,每个工作程序具有8个线程。如果没有锁定或事务块,则会按预期发生重复序列号。使用锁和事务块,所有序列号看起来都是唯一的。

答案 1 :(得分:4)

好的 - 我会直言不讳。我看不出这个的价值。如果你真的想要它,这就是你必须要做的。

首先,创建一个表org_max_post (org_id, post_id)。添加新组织时填充它(我使用数据库触发器)。

然后,在添加新帖子时,您需要:

  1. BEGIN交易
  2. SELECT FOR UPDATE该组织要锁定它的行
  3. post_id递增1,更新行。
  4. 使用该值创建帖子。
  5. COMMIT用于完成更新和释放锁定的事务。
  6. 您希望所有这一切在一个事务中发生,当然,并锁定org_max_post中的相关行。您希望确保将新的post_id分配给一个且仅一个帖子,并且如果帖子未能提交,则您不会浪费post_id。

    如果您希望变得聪明并减少应用程序代码中的SQL,您可以执行以下操作之一:

    1. 在自定义insert_post()函数中包裹上面的孔。
    2. 通过缺少post_id的视图插入,并通过规则/触发器提供。
    3. 添加一个触发器,使用正确更新的值覆盖post_id列中提供的任何内容。
    4. 删除帖子显然不会影响您的org_max_post表格,因此不会破坏您的编号。

      使用触发器阻止对数据库级别的帖子进行任何更新。检查OLD与NEW post_id中的任何更改,如果有,则抛出异常。

      然后删除posts表中现有的冗余id列,并使用(org_id,post_id)作为主键。如果你遇到这个麻烦,你也可以把它当作你的pkey使用。

      哦 - post_numpost_index可能比post_id好,因为它不是标识符。

      我不知道有多少这样可以很好地使用rails我担心 - 上次我看到它时,数据库处理是荒谬的原始。

答案 2 :(得分:0)

首先,我必须说这不是一个好习惯,但我只关注你的问题的解决方案: 您可以通过PostsController进行组织的帖子计数:

def create
  post = Post.new(...)
  ...
  post.post_id = Organization.find(organization_id).posts.count + 1
  post.save
  ...
end

您不应该自己更改数据库。让ActiveRecord来处理它。

答案 3 :(得分:0)

很高兴知道如何实现它。我更喜欢自己使用宝石。