我需要我的程序代码来确保在事务中执行某些逻辑部分。
什么查询会告诉我当前的交易 ID/其他信息,这些信息可以让我确定我是否在交易中进行交易?
BEGIN;
-- How to check if I am in a transaction?
COMMIT;
我需要我的程序代码来确保在事务中执行某些逻辑部分。
什么查询会告诉我当前的交易 ID/其他信息,这些信息可以让我确定我是否在交易中进行交易?
BEGIN;
-- How to check if I am in a transaction?
COMMIT;
一个简单的方法是比较
now()
。statement_timestamp()
now()
给出当前日期和时间(当前事务的开始)。statement_timestamp()
给出当前日期和时间(当前语句的开始)。例子:
另一种选择是执行两个查询:
并比较生成的 xid。如果相同,那么您正在交易中。
后一种方法的缺点是每次都会
txid_current()
增加 xid 值,并将进一步推动您进入wraparound。从 PostgreSQL 版本 10 及更高版本(即,截至 2022 年 5 月的所有支持版本),除了
txid_current()
@Gajus 已经提到的函数之外,还有一个txid_current_if_assigned()
函数,它返回“如果尚未分配 [transaction] ID 则返回 NULL”允许您“避免不必要地消耗 XID”(这是@Gajus 在 2018 年的回答中暗示的问题)。事实上,从 PostgreSQL 13 开始,这些
txid_
带前缀的函数已被弃用,取而代之的是pg_current_xact_id()
和pg_current_xact_id_if_assigned()
。如果你只是想知道你当前是否在事务中,那么使用它比使用更好
pg_current_xact_id_if_assigned()
的pg_current_xact_id()
原因是,根据经验,你想避免不必要的 XID 消耗。例如,GitLab遇到了问题,因为他们过于广泛地使用了保存点。但是,警告也适用于
pg_current_xact_id_if_assigned()
.NULL
只要您的事务中没有写入任何内容,它就会返回,因为 PostgreSQL 在写入操作发生之前不会分配事务 ID。一种可能的解决方法是创建一个绑定事务的临时表:你有它。
如果您在
CREATE TEMPORARY TABLE … ON COMMIT DROP
事务之外的何处运行语句,它将在每个变异语句(在自动提交模式下)执行的隐式事务结束时消失。编辑1:请注意,您不能将此逻辑包装到函数或过程中,因为函数/过程调用语句将包装在隐式事务中,因此您的
am_i_inside_a_transaction()
函数将始终返回TRUE
。编辑2:我想明确表示,除了我的答案的信息价值之外,我仍然相信@Gajus 他自己的答案的比较时间戳方法优于该
pg_current_xact_id_if_assigned()
方法。transaction_timestamp()
并且他的方法可以通过使用而不是更加明确now()
:不过,这里也有一个警告,因为
statement_timestamp()
“从客户端收到最新命令消息的时间”。这意味着如果您要将上述函数调用嵌套在另一个函数中,该函数将始终返回FALSE
。有趣的是,psql 和在应用程序中执行相同的语句会产生不同的结果。我尝试了 Tcl 脚本和 DBeaver。
陈述:
SELECT now(), statement_timestamp();
psql:
2021-05-07 13:31:10.135851+02 | 2021-05-07 13:31:10.135851+02
DBeaver:
2021-05-07 13:31:00.633784+02|2021-05-07 13:31:00.633913+02
Tcl 脚本:
{now {2021-05-07 13:24:54.659837+02} statement_timestamp {2021-05-07 13:24:54.65985+02}}
因此,在 psql now() 和 statement_timestamp() 中,当不在事务中时是相同的,而在其他情况下,这两个值略有不同,大约 100 微秒。