簡體   English   中英

具有唯一約束的原子多行更新

[英]Atomic multi-row update with a unique constraint

我有一個按排名順序顯示的標簽表。 為確保沒有兩行可以具有相同的排名,它們的值是唯一的:

create table label (
  id_label serial not null,
  rank integer not null,
  title text not null,
  constraint pri primary key (id_label),
  constraint unq unique (rank)
)

無論是PostgreSQL還是MySQL,它們都表現出相同的行為。 查詢可能看起來像select title from label order by rank 假設該表包含:

id_label rank title
1        10   Cow
2        20   Apple
3        45   Horse
4        60   Beer

現在假設我想重新排序兩個標簽,例如讓Apple排在Cow之前。 最簡單的方法是交換他們的等級值:

update label
set rank = case when rank = 20 then 10 else 20 end
where id_label in (1,2)

不。 也不:

update label
set rank = case when rank = 20 then rank - 10 else rank + 10 end
where id_label in (1,2)

甚至不是:

update label
set rank = 30 - rank
where id_label in (1,2)

每次,唯一約束觸發第一行更新並中止操作。 如果我可以將支票推遲到聲明結束,我會沒事的。 這種情況發生在PostgreSQL和MySQL上。

ACID安全的解決方法是:

  1. 開始交易
  2. 在表格中選擇第一,第二記錄和最高(最高)排名的排名(這可能需要一個聯合)
  3. 將第一條記錄更新為rank = max + 1
  4. 將第二條記錄更新為第一名
  5. 將第一條記錄更新為第二級
  6. 承諾

這簡直難以置信。 更糟糕的是刪除約束,更新,然后重新創建約束。 授予操作角色這樣的權限是一件麻煩事。 所以我的問題是:有一種我忽略的簡單技術可以解決這個問題,還是我是SOL?

使用PostgreSQL,只能使用9.0版以“漂亮”的方式解決這個問題,因為您可以定義唯一的約束,以便在那里進行延遲。

使用PostgreSQL 9.0,您只需:

create table label (
  id_label serial not null,
  rank integer not null,
  title text not null,
  constraint pri primary key (id_label)
);
alter table label add constraint unique_rank unique (rank) 
      deferrable initially immediate;

然后更新就像這樣簡單:

begin;
set constraints unique_rank DEFERRED;
update rank
   set rank = case when rank = 20 then 10 else 20 end
   where id_label in (1,2);
commit;

編輯:
如果您不想在事務中將約束設置為延遲,則可以簡單地將約束定義為initially deferred

我有類似的問題,我的解決方案如下:

  1. START TRANSACTION
  2. SELECT * FROM label WHERE id_label IN(1,2)
  3. Delete FROM label WHERE id_label IN(1,2)
  4. INSERT INTO label(all, columns, of, table) VALUES(all, values, we, selected)
  5. COMMIT TRANSACTION

如果有任何錯誤,回滾事務。

您可以執行此操作而不刪除唯一約束。

當然你可以:

update label set rank = 5 where id_label=2

但是我想這里的問題是你需要能夠處理連續排名之間沒有“差距”的情況。 對於postgres,使用numeric而不是integer來解決問題,因為它具有幾乎無限的精度

create table label (
  id_label serial not null,
  rank numeric not null,
  title text not null,
  constraint pri primary key (id_label),
  constraint unq unique (rank)
)

現在你只需更新一行就可以將它移動到排名中的任何位置,無論任何其他行的排名如何,都可以通過分割上面的等級和下面的等級之間的差異來實現。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM