如何在单个SQL语句中运行多个UPDATE而不删除唯一性验证?

时间:2015-02-22 16:20:34

标签: sql ruby-on-rails postgresql

book has_many pages。页面具有属性page_no。我想在本书的任意两个页面(x,y)之间添加PageAt(z),为此,我必须先将书籍的所有页面从y更新到book.pages.count,然后再创建 - >保存页面( Z)。

以下是单次运行更新的SQL:

sql = "update pages set page_no = page_no+1 where book_id =" + (@book.id).to_s + " and page_no >" + (@addPageAt - 1).to_s
records_arraty = ActiveRecord::Base.connection.execute(sql)

有效!但要做到这一点,我必须从pages表中删除以下验证:

validates :book_id, presence: true, on: :save
validates :page_no, presence: true, uniqueness: true, on: :save

validates_uniqueness_of :page_no, :scope => :book_id

我想保留这些验证,特别是第三个验证。怎么做?

3 个答案:

答案 0 :(得分:1)

一种方法是两个单独的更新:

update pages
    set page_no = - (page_no + 1)
    where book_id = " + (@book.id).to_s + " and page_no >" + (@addPageAt - 1).to_s;

update pages
    set page_no = - page_no
    where where book_id = " + (@book.id).to_s " and page_no < 0);

您可以在单个交易中执行此操作,因此负页码永远不可见。

答案 1 :(得分:1)

你需要:

  1. 在上面运行Gordon的SQL时使用SERIALIZABLE事务(并且让调用者期望失败(以防其他人尝试冲突的事务),从而循环重试事务。
  2. OR

    1. 在单个SQL语句中运行这两个UPDATE:

      "WITH a
      AS (
        UPDATE pages
        SET page_no = - (page_no + 1)
        WHERE book_id = " + (@book.id).to_s + "
          AND page_no > " + (@addPageAt - 1).to_s + "
        RETURNING *
      ), b AS (
        UPDATE pages
        SET page_no = - page_no
        WHERE book_id = " + (@book.id).to_s + " 
          AND page_no < 0
        RETURNING *
      )
      SELECT 1
      FROM a, b
      LIMIT 1;"
      

答案 2 :(得分:0)

您可以尝试将ORDER BY page_no DESC添加到更新查询中。从最后一个到第一个工作时,没有重复page_no