【问题标题】:Add auto increment with scope to existing column in migration-file rails将范围自动增量添加到迁移文件 rails 中的现有列
【发布时间】:2013-06-06 03:24:48
【问题描述】:

我的数据库中有帖子和组织。 Posts belongs_to 组织和组织 has_many 帖子。

我的帖子表中有一个现有的 post_id 列,现在我在创建新帖子时手动增加该列。 如何将自动增量添加到限定为 organization_id 的列?

目前我使用 mysql 作为我的数据库,但我计划切换到 PostgreSQL,所以如果可能的话,该解决方案应该适用于两者:)

非常感谢!

【问题讨论】:

  • 1.您所说的“自动增量......范围为组织ID”到底是什么意思? 2. 为什么要它(关心人工密钥的精确值通常没有帮助)?
  • 我希望 post_id 依赖于组织。假设我们有两个组织。组织一创建第一个帖子: id: 1 post_id: 1 title: "Hello world" organization_id: 1 然后组织二创建一个帖子: id: 2 post_id: 1 title: "Hello another world" organization_id: 2 所以 post_id 将当另一个组织发布新职位时,不会增加。基本上就是这个想法。当您从组织 2 中删除帖子 1 时,下一个 post_id 当然应该是 2。所以基本上post_id不会根据其他组织的帖子而增加。
  • 你还没有解决“为什么?”在我的评论中。另外,我不清楚您是否发现编号中的漏洞可以接受。或者如果您尝试更新 post_id 会发生什么。您是否意识到尝试做此类事情时的各种并发/组织问题?这个问题的简短回答是“不要那样做”,但这本身并不是很有帮助。所以 - 你需要解释你为什么要这样做,以便有人可以提出更好的方法。
  • 感谢您的回复。首先,是的,我们的 post_id 中的漏洞是可以接受的。这是强制性的。其次,我们公司需要这样做的原因是,我们希望 post_id 绝对唯一,并且我们不想在视图中显示主键(id)本身作为后指示符,因为我们希望组织范围。我并不是说我们使用 post_id 来指代帖子。当然,我们使用 id 本身。所以基本上post_id只是为了用户的利益,需要在视图中显示为post指示符。

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


【解决方案1】:

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

【讨论】:

【解决方案2】:

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

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

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 个线程)来创建订单。如果没有锁或事务块,重复的序列号会按预期发生。有了锁和事务块,所有的序列号看起来都是唯一的。

【讨论】:

    【解决方案3】:

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

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

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

    1. BEGIN一笔交易
    2. SELECT FOR UPDATE该组织的行锁定它
    3. post_id 加一,更新行。
    4. 使用该值创建您的帖子。
    5. COMMIT 完成更新和释放锁的事务。

    当然,您希望所有这些都发生在单个事务中,并且锁定 org_max_post 中的相关行。您要确保将新的 post_id 分配给一个且仅一个帖子,并且如果该帖子未能提交,您不会浪费 post_id。

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

    1. 在自定义 insert_post() 函数中封装上面的孔。
    2. 通过缺少 post_id 并通过规则/触发器提供的视图插入。
    3. 添加一个触发器,用正确更新的值覆盖 post_id 列中提供的任何内容。

    删除帖子显然不会影响您的org_max_post 表,因此不会破坏您的编号。

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

    然后删除您的帖子表中现有的冗余 id 列并使用 (org_id,post_id) 作为您的主键。如果您遇到这个麻烦,不妨将它用作您的 pkey。

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

    恐怕我不知道其中有多少可以很好地与 rails 配合使用 - 我上次查看它时,数据库处理非常原始。

    【讨论】:

    • 非常感谢。我想我会采用那个解决方案。我知道做我们想做的事似乎是不好的做法,但这是我们认为在我们的系统中强制执行的事情。是的,数据库处理看起来很原始,但我真的很欣赏 Active Records 和 ROR 中内置的记录关联等。我不认为这将很难实施。你能告诉我这个例子中交易的想法吗?我只是好奇:)
    • 添加了一些关于事务/锁定以及如何包装流程的注释。
    【解决方案4】:

    首先,我必须说这不是一个好习惯,但我只会专注于解决您的问题: 您始终可以通过在 PostsController 上执行操作来获取组织的帖子数:

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

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

    【讨论】:

    • 感谢您的回答 :) 我遇到了这种方法。这个解决方案的问题是,我们希望 post_id “永远”是绝对唯一的,就像自动递增的主键一样。因此,如果我有 6 个帖子,我删除了最后一个帖子,然后创建了一个新帖子,它应该有 post_id: 7 而不是 6 像你的例子。所以它会是这样的:1,2,3,4,5,7
    猜你喜欢
    • 2014-09-30
    • 1970-01-01
    • 2016-05-29
    • 2015-06-05
    • 2019-01-18
    • 2016-07-23
    • 2013-01-23
    相关资源
    最近更新 更多