在PostgreSQL中,每一个操作都是一个事务。
SELECT
now(),
now();
now | now
-------------------------------+-------------------------------
2020-02-29 07:21:55.100997+00 | 2020-02-29 07:21:55.100997+00
(1 row)
now()
函数返回该事务的时间。在这种情况下,SELECT
语句是一个单独的事务,因此,总是返回两个相同的时间戳。
如果要让多个语句作为同一个事务的一部分,就必须使用BEGIN
子句:
postgres=# BEGIN;
BEGIN
SELECT
now();
now
-------------------------------
2020-02-29 07:15:06.975875+00
(1 row)
SELECT
now();
now
-------------------------------
2020-02-29 07:15:06.975875+00
(1 row)
postgres=# COMMIT;
COMMIT
postgres=# BEGIN;
BEGIN
SELECT
txid_current();
txid_current
--------------
697
(1 row)
SELECT
txid_current();
txid_current
--------------
697
(1 row)
postgres=# COMMIT;
COMMIT
在事务内部处理错误
在PostgreSQL中,只有没有错误的事务才能被提交。
postgres=# BEGIN;
BEGIN
SELECT
1;
?column?
----------
1
(1 row)
SELECT
1 / 0;
ERROR: division by zero
SELECT
1;
ERROR: current transaction is aborted, commands ignored until end of transaction block
postgres=# COMMIT;
ROLLBACK
在一个错误出现后,即便后面的指令在语义和语法上都是正确的,也将不会再有语句被接受。这种情况下,仍然可以发出一个COMMIT
。不过,PostgreSQL将回滚该事务。
使用保存点
在专业应用中,很难写出长度合理的事务而且在运行中永不出错。为了解决这一问题,可以使用SAVEPOINT
。
postgres=# BEGIN;
BEGIN
SELECT
1;
?column?
----------
1
(1 row)
postgres=# SAVEPOINT a;
SAVEPOINT
SELECT
2 / 0;
ERROR: division by zero
postgres=# ROLLBACK TO SAVEPOINT a;
ROLLBACK
SELECT
3;
?column?
----------
3
(1 row)
postgres=# COMMIT;
COMMIT
事务性DDL
在PostgreSQL中,可以在一个事务块中运行DDL(改变数据结构的命令)。在一个典型的商业系统中,在当前事务中的DDL将会被隐式地提交,但在PostgreSQL中不是这样。除去少量例外(DROP DATABSE
,CREATE TABLESPACE
/DROP TABLESPACE
等),PostgreSQL中所有的DDL都是事务性的。
postgres=# BEGIN;
BEGIN
CREATE TABLE test (
id int
);
CREATE TABLE
postgres=# \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
public | test | table | postgres
(1 row)
ALTER TABLE
test
ALTER COLUMN
id TYPE int8;
ALTER TABLE
postgres=# \d test
Table "public.test"
Column | Type | Modifiers
--------+--------+-----------
id | bigint |
postgres=# ROLLBACK;
ROLLBACK
postgres=# \d
No relations found.
如果用户想要部署软件,事务性DDL特别重要。想象运行一个CMS的场景,如果发布了一个新的版本,用户将会想要升级。单独运行旧版本或者新版本可能都是可以的,但是用户不希望得到新旧版本的混合体。因此,将升级放在一个单一事务内无疑会大有益处,因为这会使升级变成一个原子操作。
psql允许用户使用i
指令包括文件。它允许用户开始一个事务、载入一些文件并且在一个单一事务中执行它们。