【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