PostgreSQL中實現(xiàn)Update前的備份騷操作

參考文章:【PostgreSQL 如何實現(xiàn)upsert與新舊數(shù)據(jù)自動分離】

背景

很多業(yè)務(wù)也行有這樣的需求复旬,新的數(shù)據(jù)會不斷的插入,并且可能會有更新缰犁。 對于更新的數(shù)據(jù)腺逛,需要記錄更新前的記錄到歷史表。 這個需求有點類似于審計需求纱扭,即需要對記錄變更前后做審計牍帚。 本文的目的并不是審計,而且也可能不期望使用觸發(fā)器跪但。

還有什么方法呢履羞?
PostgreSQL 這么高大上,當然有屡久,而且還能在一句SQL里面完成忆首,看法寶。

騷操作

創(chuàng)建一張當前狀態(tài)表被环,一張歷史記錄表糙及。

postgres=# create table tbl(id int primary key, price int);
CREATE TABLE
postgres=# create table tbl_history (id int not null, price int);
CREATE TABLE

插入一條不存在的記錄,不會觸發(fā)插入歷史表的行為筛欢。
注意替代變量

id=$1 = 2
price=$2 = 7

postgres=# with old as (select * from tbl where id= $1), 
postgres-# new as (insert into tbl values ($1, $2) on conflict (id) do update set price=excluded.price where tbl.price<>excluded.price returning *) 
postgres-# insert into tbl_history select old.* from old,new where old.id=new.id;
INSERT 0 0

postgres=# select tableoid,ctid,* from tbl union all select tableoid,ctid,* from tbl_history ;
 tableoid | ctid  | id | price 
----------+-------+----+-------
    18243 | (0,1) |  2 |     7
(1 row)

插入一條不存在的記錄浸锨,不會觸發(fā)插入歷史表的行為唇聘。

id=$1 = 1
price=$2 = 1

postgres=# with old as (select * from tbl where id= $1), 
new as (insert into tbl values ($1, $2) on conflict (id) do update set price=excluded.price where tbl.price<>excluded.price returning *) 
insert into tbl_history select old.* from old,new where old.id=new.id;
INSERT 0 0
postgres=# select tableoid,ctid,* from tbl union all select tableoid,ctid,* from tbl_history ;
 tableoid | ctid  | id | price 
----------+-------+----+-------
    18243 | (0,1) |  2 |     7
    18243 | (0,2) |  1 |     1
(2 rows)

插入一條已存在的記錄,并且有數(shù)據(jù)的變更柱搜,觸發(fā)數(shù)據(jù)插入歷史表的行為迟郎。

id=$1 = 1
price=$2 = 2

postgres=# with old as (select * from tbl where id= $1), 
new as (insert into tbl values ($1, $2) on conflict (id) do update set price=excluded.price where tbl.price<>excluded.price returning *) 
insert into tbl_history select old.* from old,new where old.id=new.id;
INSERT 0 1
postgres=# select tableoid,ctid,* from tbl union all select tableoid,ctid,* from tbl_history ;
 tableoid | ctid  | id | price 
----------+-------+----+-------
    18243 | (0,1) |  2 |     7
    18243 | (0,3) |  1 |     2
    18251 | (0,1) |  1 |     1
(3 rows)

插入一條已存在的記錄,并且已存在的記錄值和老值一樣聪蘸,不會觸發(fā)將數(shù)據(jù)插入歷史表的行為宪肖。

id=$1 = 1
price=$2 = 2

postgres=# with old as (select * from tbl where id= $1), 
new as (insert into tbl values ($1, $2) on conflict (id) do update set price=excluded.price where tbl.price<>excluded.price returning *) 
insert into tbl_history select old.* from old,new where old.id=new.id;
INSERT 0 0
postgres=# select tableoid,ctid,* from tbl union all select tableoid,ctid,* from tbl_history ;
 tableoid | ctid  | id | price 
----------+-------+----+-------
    18243 | (0,1) |  2 |     7
    18243 | (0,3) |  1 |     2
    18251 | (0,1) |  1 |     1
(3 rows)

執(zhí)行計劃

postgres=# explain with old as (select * from tbl where id= $1), 
new as (insert into tbl values ($1, $2) on conflict (id) do update set price=excluded.price where tbl.price<>excluded.price returning *) 
insert into tbl_history select old.* from old,new where old.id=new.id;
                                 QUERY PLAN                                 
----------------------------------------------------------------------------
 Insert on tbl_history  (cost=2.17..2.23 rows=1 width=8)
   CTE old
     ->  Index Scan using tbl_pkey on tbl  (cost=0.14..2.16 rows=1 width=8)
           Index Cond: (id = 1)
   CTE new
     ->  Insert on tbl tbl_1  (cost=0.00..0.01 rows=1 width=8)
           Conflict Resolution: UPDATE
           Conflict Arbiter Indexes: tbl_pkey
           Conflict Filter: (tbl_1.price <> excluded.price)
           ->  Result  (cost=0.00..0.01 rows=1 width=8)
   ->  Nested Loop  (cost=0.00..0.05 rows=1 width=8)
         Join Filter: (old.id = new.id)
         ->  CTE Scan on old  (cost=0.00..0.02 rows=1 width=8)
         ->  CTE Scan on new  (cost=0.00..0.02 rows=1 width=4)
(14 rows)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市健爬,隨后出現(xiàn)的幾起案子控乾,更是在濱河造成了極大的恐慌,老刑警劉巖娜遵,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜕衡,死亡現(xiàn)場離奇詭異,居然都是意外死亡设拟,警方通過查閱死者的電腦和手機慨仿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒜绽,“玉大人镶骗,你說我怎么就攤上這事桶现《阊牛” “怎么了?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵骡和,是天一觀的道長相赁。 經(jīng)常有香客問我,道長慰于,這世上最難降的妖魔是什么钮科? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮婆赠,結(jié)果婚禮上绵脯,老公的妹妹穿的比我還像新娘。我一直安慰自己休里,他們只是感情好蛆挫,可當我...
    茶點故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妙黍,像睡著了一般悴侵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拭嫁,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天可免,我揣著相機與錄音抓于,去河邊找鬼。 笑死浇借,一個胖子當著我的面吹牛捉撮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妇垢,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼呕缭,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了修己?” 一聲冷哼從身側(cè)響起恢总,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎睬愤,沒想到半個月后片仿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡尤辱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年砂豌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片光督。...
    茶點故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡阳距,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出结借,到底是詐尸還是另有隱情筐摘,我是刑警寧澤,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布船老,位于F島的核電站咖熟,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏柳畔。R本人自食惡果不足惜馍管,卻給世界環(huán)境...
    茶點故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望薪韩。 院中可真熱鬧确沸,春花似錦、人聲如沸俘陷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岭洲。三九已至宛逗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盾剩,已是汗流浹背雷激。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工替蔬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屎暇。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓承桥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親根悼。 傳聞我的和親對象是個殘疾皇子凶异,可洞房花燭夜當晚...
    茶點故事閱讀 43,499評論 2 348