PostgreSQL update returning NEW|OLD column value 在对账|购票|防纂改|原子操作中的妙用

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB PostgreSQL 版,企业版 4核16GB
推荐场景:
HTAP混合负载
云原生数据库 PolarDB MySQL 版,通用型 2核4GB 50GB
简介: 标签 PostgreSQL , update , returning , NEW , OLD 背景 在数据库中更新记录时,有时为了对账,或者防纂改的目的,需要在更新后立即返回更新前和更新后的值。 例如以set bit为例,假设使用BIT串作为火车的每个位置,每个BIT代表一张票,0表示未售卖,1表示已售卖。 购票时,使用set bit=1的操作,为了确保不出现重复售票的问题,必须确保

标签

PostgreSQL , update , returning , NEW , OLD


背景

在数据库中更新记录时,有时为了对账,或者防纂改的目的,需要在更新后立即返回更新前和更新后的值。

例如以set bit为例,假设使用BIT串作为火车的每个位置,每个BIT代表一张票,0表示未售卖,1表示已售卖。

购票时,使用set bit=1的操作,为了确保不出现重复售票的问题,必须确保被set的value以前的值为0,SET后的值为1。

这个动作其实也可以在function中来保证,不过你要扩展FUNCTION的功能,在function中确保set bit前的值为0,set bit后的值为1。

本文要讲的是通过update returning语法来实现类似的对照功能。

其实insert returning也有类似的用法,例如插入时并不知道数据库生成了什么UUID,这个UUID可能是流水号,将来程序要用来做二次确认的搜索。(如运营商的二次确认,或者短信密码,也需要用来作为标识)

update returning语法

目前PostgreSQL支持insert,delete,update的returning。

insert returning 返回的是新插入的值。

delete returning 返回的是被删除的值。

update returning 返回的是更新后的值,不能返回更新前的值,但是有方法可以得到。

或者等阿里云RDS PostgreSQL退出update returning old.column的功能吧。

例子

PostgreSQL 支持delete, update返回删除前的值以及更新后的值.

postgres=# create table test (old text, new text, mod_time timestamp);
CREATE TABLE
postgres=# insert into test values ('old', 'new', now());
INSERT 0 1
postgres=# select * from test ;
 old | new |          mod_time          
-----+-----+----------------------------
 old | new | 2013-01-22 15:36:02.543393
(1 row)

postgres=# update test set new='DIGOAL', old=new, mod_time=clock_timestamp() returning *;
 old |  new   |          mod_time          
-----+--------+----------------------------
 new | DIGOAL | 2013-01-22 15:36:40.062419
(1 row)
UPDATE 1

update returning 返回的是更新后的值.

postgres=# select * from test ;
 old |  new   |          mod_time          
-----+--------+----------------------------
 new | DIGOAL | 2013-01-22 15:36:40.062419
(1 row)

postgres=# delete from test returning *;
 old |  new   |          mod_time          
-----+--------+----------------------------
 new | DIGOAL | 2013-01-22 15:36:40.062419
(1 row)
DELETE 1

delete returning 返回的是删除前的值.

returning 后的子句类似select ... from 中的子句, 所以也支持表达式 :

postgres=# insert into test values ('old', 'new', now());
INSERT 0 1
postgres=# update test set new='DIGOAL', old=new, mod_time=clock_timestamp() returning 1,2,3,old,new,mod_time,old||new;
 ?column? | ?column? | ?column? | old |  new   |          mod_time          | ?column?  
----------+----------+----------+-----+--------+----------------------------+-----------
        1 |        2 |        3 | new | DIGOAL | 2013-01-22 15:39:13.238924 | newDIGOAL
(1 row)
UPDATE 1

update returning 如何返回old.column

方法1, update 中, 如果将一个字段的值赋予给另一个字段, 那会将更新前的值赋予给它, 而不是更新后的值.

postgres=# update test set new='DIGOAL', old=new, mod_time=clock_timestamp() returning 1,2,3,old,new,mod_time,old||new;
 ?column? | ?column? | ?column? | old |  new   |          mod_time          | ?column?  
----------+----------+----------+-----+--------+----------------------------+-----------
        1 |        2 |        3 | new | DIGOAL | 2013-01-22 15:39:13.238924 | newDIGOAL
(1 row)
UPDATE 1

new='DIGOAL', old=new

更新后 :   

old = 'new' (new字段更新前的值)
new = 'DIGOAL'

使用这种方法可以返回set_bit前的bit string以及set_bit后的bit string。

方法2, 如果被更新的表有PK,可以使用update from子句, 返回旧值

UPDATE tbl x
SET    tbl_id = 23
     , name = 'New Guy'
FROM   tbl y                -- using the FROM clause
WHERE  x.tbl_id = y.tbl_id  -- must be unique
AND    x.tbl_id = 3
RETURNING y.tbl_id AS old_id, y.name AS old_name
        , x.tbl_id          , x.name;
Returns:

 old_id | old_name | tbl_id |  name
--------+----------+--------+---------
  3     | Old Guy  | 23     | New Guy

UPDATE tbl x
SET    tbl_id = 24
     , name = 'New Gal'
FROM  (SELECT tbl_id, name FROM tbl WHERE tbl_id = 4 FOR UPDATE) y 
WHERE  x.tbl_id = y.tbl_id
RETURNING y.tbl_id AS old_id, y.name AS old_name, x.tbl_id, x.name;

使用这种方法也可以返回set_bit前的bit string以及set_bit后的bit string。

参考

1. http://www.postgresql.org/docs/9.2/static/sql-delete.html

2. http://www.postgresql.org/docs/9.2/static/sql-update.html

3. http://stackoverflow.com/questions/7923237/return-pre-update-column-values-using-sql-only-postgresql-version

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
2月前
|
SQL Oracle 关系型数据库
实时计算 Flink版操作报错之往GREENPLUM 6 写数据,用postgresql-42.2.9.jar 报 ON CONFLICT (uuid) DO UPDATE SET 语法有问题。怎么解决
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
8月前
|
关系型数据库 PostgreSQL
postgresql通过select结果进行update
postgresql通过select结果进行update
|
弹性计算 关系型数据库 PostgreSQL
PostgreSQL PostGIS 性能提升 - by new GEOS代码
标签 PostgreSQL , PostGIS , geos 背景 http://lin-ear-th-inking.blogspot.com/2019/02/betterfaster-stpointonsurface-for.html 使用GEOS新的代码,提升PostGIS重计算的函数性能。 The improved ST_PointOnSurface runs 13 times
735 0
|
弹性计算 关系型数据库 测试技术
PostgreSQL 分区表如何支持多列唯一约束 - 枚举、hash哈希 分区, 多列唯一, insert into on conflict, update, upsert, merge insert
标签 PostgreSQL , 分区表 , native partition , 唯一 , 非分区键唯一 , 组合唯一 , insert into on conflict , upsert , merge insert 背景 PG 11开始支持HASH分区,10的分区如果要支持hash分区,可以通过枚举绕道实现。 《PostgreSQL 9.x, 10, 11 hash分区表 用法举例
2970 0
|
SQL 关系型数据库 PostgreSQL
PostgreSQL Heap Only Tuple - HOT (降低UPDATE引入的索引写IO放大)
标签 PostgreSQL , Heap Only Tuple , HOT 背景 PostgreSQL目前默认的存储引擎在更新记录时,会在堆内产生一条新版本,旧版本在不需要使用后VACUUM回收,回收旧版本前,需要先回收所有关联这个版本的所有索引POINT。
2488 0
|
关系型数据库 PostgreSQL
PostgreSQL merge insert(upsert/insert into on conflict) 如何区分数据是INSERT还是UPDATE
标签 PostgreSQL , merge insert , upsert , insert into on conflict , 区分 insert update , xmin , xmax 背景 使用insert into on conflict update语法,可以支持UPSERT的功能,但是到底这条SQL是插入的还是更新的呢?如何判断 通过xmax字段的值是否不为0,可以判断,如果是UPDATE,XMAX里面会填充更新事务号。
2091 0
|
关系型数据库 PostgreSQL 测试技术

热门文章

最新文章