【7】PostgreSQL 事务

【7】PostgreSQL 事务

前言

PostgreSQL 中,每一个操作都是一个事务。即使一个简单的查询 (select),这也是一个事务。

例如:

sql 复制代码
postgres=# select now();
              now              
-------------------------------
 2025-07-07 09:08:11.218095+08
(1 row)

postgres=# 

上述的 select 语句将是一个单独的事物。

使用事务

PostgreSQL 中,如果想要让多个语句作为同一个事务的一部分,需使用 begin 子句。

begin 语法如下:

sql 复制代码
postgres=# \h begin;
Command:     BEGIN
Description: start a transaction block
Syntax:
BEGIN [ WORK | TRANSACTION ] [ transaction_mode [, ...] ]

where transaction_mode is one of:

    ISOLATION LEVEL { SERIALIZABLE | REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED }
    READ WRITE | READ ONLY
    [ NOT ] DEFERRABLE
postgres=# 

begin 子句将多个 SQL 命令包装到一个事务中,示例:

sql 复制代码
postgres=# begin;
BEGIN
postgres=# 
postgres=# select now();
              now              
-------------------------------
 2025-07-07 09:15:03.335124+08
(1 row)

postgres=#   ### 这里等待了好多秒,
postgres=#   ### 再次执行 select now() 查询的时间一致。
postgres=#
postgres=# select now();
              now              
-------------------------------
 2025-07-07 09:15:03.335124+08
(1 row)

postgres=# 

上述的 begin 语句示例中,需要注意的 两次 select 查询之间其实等待了好多秒(即:第一个 select 查询后,等待好多秒后,再次执行第二个 select 语句)。

在同一个事务中,两次 select 查询之间即使等待好多秒,两次查询结果也是一致的

commit 语法如下:

要结束一个事务,可使用 commit 语句

sql 复制代码
postgres=# \h commit
Command:     COMMIT
Description: commit the current transaction
Syntax:
COMMIT [ WORK | TRANSACTION ]

postgres=# 

commit 子句提交一个事务:

sql 复制代码
postgres=# commit;
COMMIT
postgres=# 

另外,在 PostgreSQL 中除了使用 commit 语句提交事务外,还可使用 end 语句。

end 语法如下:

sql 复制代码
postgres=# \h end;
Command:     END
Description: commit the current transaction
Syntax:
END [ WORK | TRANSACTION ]

postgres=# 
postgres=# 
postgres=# ### 和 commit 子句做一个对比,语法是一样的。
postgres=# \h commit;
Command:     COMMIT
Description: commit the current transaction
Syntax:
COMMIT [ WORK | TRANSACTION ]

postgres=# 

提交 (commit | end) 对应的命令,有个 rollback

rollback 语法如下:

sql 复制代码
postgres=# \h rollback;
Command:     ROLLBACK
Description: abort the current transaction
Syntax:
ROLLBACK [ WORK | TRANSACTION ]

postgres=# 

注意

  • rollback 并不是成功的结束一个事务,仅会停止事务 而不把事务中的部分对其他事务可见。【见描述:abort the current transaction
  • rollback 含义相同的还有一个 abort 语句

abort 语法如下:

sql 复制代码
postgres=# \h abort
Command:     ABORT
Description: abort the current transaction
Syntax:
ABORT [ WORK | TRANSACTION ]

postgres=# 

事务内错误处理

PostgreSQL 中,只有 没有发生错误的事务 才能被提交

如下示例

访问一个不存在的函数 non_existent_function() 故意制作报错,查看数据库表现。

sql 复制代码
postgres=# CREATE TABLE tb_test (
postgres(#     id INT
postgres(# );
CREATE TABLE
postgres=# 
postgres=# begin;
BEGIN
postgres=# insert into tb_test(id) values(1001);
INSERT 0 1
postgres=# 
postgres=# select * from tb_test;
  id  
------
 1001
(1 row)
postgres=# 
postgres=#  ### 这里访问一个不存在的函数,故意制作报错。
postgres=# SELECT non_existent_function();
ERROR:  function non_existent_function() does not exist
LINE 1: SELECT non_existent_function();
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
postgres=# 
postgres=# 
postgres=# select * from tb_test;
ERROR:  current transaction is aborted, commands ignored until end of transaction block
postgres=# 
postgres=# commit;
ROLLBACK
postgres=# 
postgres=# select * from tb_test;
 id 
----
(0 rows)

postgres=# 

上述同样的操作步骤,看下 在 MySQL 中的表现

MySQL 数据库版本:

sql 复制代码
[root@localhost][testdb]> select version();
+------------+
| version()  |
+------------+
| 5.7.26-log |
+------------+
1 row in set (0.00 sec)

在 MySQL 数据库中 的验证 SQL 语句:

sql 复制代码
[root@localhost][testdb]> CREATE TABLE tb_test (
    ->     id INT
    -> );
Query OK, 0 rows affected (0.01 sec)

[root@localhost][testdb]> begin;
Query OK, 0 rows affected (0.00 sec)

[root@localhost][testdb]> insert into tb_test(id) values(1001);
Query OK, 1 row affected (0.00 sec)

[root@localhost][testdb]> select * from tb_test;
+------+
| id   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)

[root@localhost][testdb]> SELECT non_existent_function(); 
ERROR 1305 (42000): FUNCTION testdb.non_existent_function does not exist
[root@localhost][testdb]> 
[root@localhost][testdb]> select * from tb_test;
+------+
| id   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)

[root@localhost][testdb]> commit;
Query OK, 0 rows affected (0.00 sec)

[root@localhost][testdb]> 
[root@localhost][testdb]> select * from tb_test;
+------+
| id   |
+------+
| 1001 |
+------+
1 row in set (0.00 sec)

[root@localhost][testdb]> 

从上述的验证结果中,可知:

与 MySQL 不同,PostgreSQL 数据库在出现错误后,即使后续语句在语法和语义上完全正确,也不会再接受任何语句。即便此时发出 COMMIT 语句,PostgreSQL 也会回滚整个事务。

事务保存点

在 PostgreSQL 数据库中,保存点使用 savepoint 子句,语法如下:

sql 复制代码
postgres=# \h savepoint;
Command:     SAVEPOINT
Description: define a new savepoint within the current transaction
Syntax:
SAVEPOINT savepoint_name

postgres=# 

演示- 示例(01)

如下示例中,即使有保存点,当遇到报错时,进行提交。

此时也不会从保存点保存数据,因为 :在 PostgreSQL 中,只有 没有发生错误的事务 才能被提交

sql 复制代码
postgres=# select * from tb_test;
 id 
----
(0 rows)

postgres=# begin;
BEGIN
postgres=# insert into tb_test values(1001);
INSERT 0 1
postgres=# 
postgres=# select * from tb_test;
  id  
------
 1001
(1 row)

postgres=# savepoint step01;
SAVEPOINT
postgres=# 
postgres=# select 1/0;
ERROR:  division by zero
postgres=# 
postgres=# select * from tb_test;
ERROR:  current transaction is aborted, commands ignored until end of transaction block
postgres=# 
postgres=# commit;
ROLLBACK
postgres=# 
postgres=# select * from tb_test;
 id 
----
(0 rows)

postgres=# 

演示- 示例(02)

如下示例中,有保存点,当遇到报错时,回退到保存点 进行提交。

这种情况下,事务可以提交成功。

sql 复制代码
postgres=# select * from tb_test;
 id 
----
(0 rows)

postgres=# begin;
BEGIN
postgres=# insert into tb_test values(1001);
INSERT 0 1
postgres=# savepoint step01;
SAVEPOINT
postgres=# select * from tb_test;
  id  
------
 1001
(1 row)

postgres=# select 1/0;
ERROR:  division by zero
postgres=# 
postgres=# select * from tb_test;
ERROR:  current transaction is aborted, commands ignored until end of transaction block
postgres=# 
postgres=# rollback to savepoint step01;
ROLLBACK
postgres=# 
postgres=# select * from tb_test;
  id  
------
 1001
(1 row)

postgres=# commit;
COMMIT
postgres=# 
postgres=# select * from tb_test;
  id  
------
 1001
(1 row)

postgres=# 

演示- 示例(03)

如下示例中,事务提交后,再尝试**回退到保存点** 。

sql 复制代码
postgres=# select * from tb_test;
  id  
------
 1001
(1 row)

postgres=# 
postgres=# begin;
BEGIN
postgres=# insert into tb_test values(1002);
INSERT 0 1
postgres=# savepoint step01;
SAVEPOINT
postgres=# select * from tb_test;
  id  
------
 1001
 1002
(2 rows)

postgres=# commit;
COMMIT
postgres=# rollback to savepoint step01;
ERROR:  ROLLBACK TO SAVEPOINT can only be used in transaction blocks
postgres=# 

在事务已经被结束之后,将无法再次返回到一个之前的保存点。

DDL 事务

在 PostgreSQL 数据库中,除了少量(drop database | create tablespace/drop tablespace 等),PostgreSQL中所有的 DDL 都是事务性的。

例如

在一个事务块中运行 DDL(改变数据结构命令),在MySQL中,当前事务中的 DDL 将会被隐式提交。但在 PostgreSQL 数据库中,可回滚。

MySQL 数据库,示例验证:

sql 复制代码
[root@localhost][testdb]> desc tb_test;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)

[root@localhost][testdb]> 
[root@localhost][testdb]> begin;
Query OK, 0 rows affected (0.00 sec)

[root@localhost][testdb]> ALTER TABLE tb_test 
    -> MODIFY COLUMN id CHAR(10);
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

[root@localhost][testdb]> rollback;
Query OK, 0 rows affected (0.00 sec)

[root@localhost][testdb]> 
[root@localhost][testdb]> desc tb_test;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id    | char(10) | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
1 row in set (0.00 sec)

[root@localhost][testdb]> 

PostgreSQL 数据库,示例验证:

sql 复制代码
postgres=# \d tb_test;
    Table "public.tb_test"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | 

postgres=# 
postgres=# begin;
BEGIN
postgres=# 
postgres=# ALTER TABLE tb_test 
postgres-# ALTER COLUMN id TYPE CHAR(10);
ALTER TABLE
postgres=# 
postgres=# \d tb_test;
       Table "public.tb_test"
 Column |     Type      | Modifiers 
--------+---------------+-----------
 id     | character(10) | 

postgres=# 
postgres=# rollback;
ROLLBACK
postgres=# 
postgres=# \d tb_test;
    Table "public.tb_test"
 Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | 

postgres=# 

若有转载,请标明出处:
https://blog.csdn.net/CharlesYuangc/article/details/149165365

相关推荐
一屉大大大花卷39 分钟前
初识Neo4j之入门介绍(一)
数据库·neo4j
周胡杰1 小时前
鸿蒙arkts使用关系型数据库,使用DB Browser for SQLite连接和查看数据库数据?使用TaskPool进行频繁数据库操作
前端·数据库·华为·harmonyos·鸿蒙·鸿蒙系统
wkj0011 小时前
navicate如何设置数据库引擎
数据库·mysql
赵渝强老师1 小时前
【赵渝强老师】Oracle RMAN的目录数据库
数据库·oracle
暖暖木头1 小时前
Oracle注释详解
数据库·oracle
御控工业物联网2 小时前
御控网关如何实现MQTT、MODBUS、OPCUA、SQL、HTTP之间协议转换
数据库·sql·http
GJCTYU3 小时前
spring中@Transactional注解和事务的实战理解附代码
数据库·spring boot·后端·spring·oracle·mybatis
MicroTech20253 小时前
微算法科技(NASDAQ: MLGO)探索Grover量子搜索算法,利用量子叠加和干涉原理,实现在无序数据库中快速定位目标信息的效果。
数据库·科技·算法
Code季风3 小时前
SQL关键字快速入门:CASE 实现条件逻辑
javascript·数据库·sql
weixin_478689763 小时前
操作系统【2】【内存管理】【虚拟内存】【参考小林code】
数据库·nosql